package org.javahispano.javacup.tacticas_aceptadas.DaniP;

import java.awt.Color;
import java.io.File;
import java.io.PrintStream;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.Collections;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import java.util.Vector;

import org.javahispano.javacup.modelo.Comando;
import org.javahispano.javacup.modelo.ComandoGolpearBalon;
import org.javahispano.javacup.modelo.ComandoIrA;
import org.javahispano.javacup.modelo.Constantes;
import org.javahispano.javacup.modelo.EstiloUniforme;
import org.javahispano.javacup.modelo.JugadorDetalle;
import org.javahispano.javacup.modelo.Posicion;
import org.javahispano.javacup.modelo.SituacionPartido;
import org.javahispano.javacup.modelo.Tactica;
import org.javahispano.javacup.modelo.TacticaDetalle;
import org.javahispano.javacup.modelo.Comando.Tipo;

public class Termineitor implements Tactica
{
  protected static boolean SILENT_MODE = true;
//  protected static boolean SHOW_COMANDOS_INFO = false;
//  protected static boolean SILENT_MODE = false;
  protected static boolean SHOW_COMANDOS_INFO = true;
  protected static boolean SHOW_COMANDOS_INFO_1 = true;
  protected static boolean SHOW_COMANDOS_INFO_10 = true;
  protected static boolean SHOW_COMANDOS_INFO_100 = true;
  protected static boolean SHOW_INFO = true;
  protected static boolean SHOW_INFO_BALON = true;
  protected static boolean SHOW_INFO_BALON_CALCULAR_DESTINO = true;
  protected static boolean SHOW_INFO_BALON_CALCULAR_DESTINO_ITERANDO = true;
  protected static boolean SHOW_INFO_PUEDE_REMATAR = false;
  protected static boolean SHOW_INFO_PUEDE_REGATEAR = true;  
  
  protected static boolean SHOW_COMANDOS_INICIAL = true;
  protected static boolean SHOW_COMANDOS_INICIAL_RIVALES = true;
  protected static boolean SHOW_COMANDOS_MOVIMIENTO = true;
  protected static boolean SHOW_COMANDOS_PASE = true;
  protected static boolean SHOW_COMANDOS_REGATE = true;
  protected static boolean SHOW_COMANDOS_TIRO = true;
  protected static boolean SHOW_COMANDOS_SAQUE = true;
  protected static boolean SHOW_COMANDOS_JUGADA = true;
  
  protected static int COMANDO_DEBUG = -2;  // Por defecto No ser� imprimido
  protected static int COMANDO_TODOS = -1;
  protected static int COMANDO_INICIAL = 0;
  protected static int COMANDO_MOVIMIENTO = 1;
  protected static int COMANDO_PASE = 2;
  protected static int COMANDO_REGATE = 3;
  protected static int COMANDO_TIRO = 4;
  protected static int COMANDO_SAQUE = 5;
  protected static int COMANDO_JUGADA = 6;

  /**
   * LIMITES TACTICOS
   */
  public static double MOVIMIENTO_MEDIOS_ATADELANTE = 15;
  public static double MOVIMIENTO_MEDIOS_ATRAS = -15;
  public static double MOVIMIENTO_ATACANTES_ATRAS = -12;
  public static double MOVIMIENTO_DESMARQUE_LIMITE = 4;
  public static double MOVIMIENTO_DESMARQUE_RIVAL_CERCA = 2;

  public static double ACHIQUE_LIMITE = 30;
//  public static double ACHIQUE_LIMITE = 10;
  public static double ACHIQUE_LIMITE_PORTERO = 5;
  public static int ACHIQUE_LIMITE_JUGADORES = 3;
  public static boolean ACHIQUE_POR_CUADRANTES = true;
//  public static int ACHIQUE_LIMITE_JUGADORES = 11;
  public static boolean ACHIQUE_FORZOSO = true;
  public static boolean ACHIQUE_ANTICIPADO = true;
  public static double ACHIQUE_ANTICIPADO_VELOCIDAD_MINIMA = 0.5;
  // Achique adelantado al rival
  public static boolean ACHIQUE_ANTICIPADO_RIVALES = true;
  public static int ACHIQUE_ANTICIPADO_RIVALES_LIMITE = 4;  //2;
  public static int ACHIQUE_ANTICIPADO_RIVALES_CERCA_PELOTA = 10;
  public static double PASE_RIVAL_ANGULO_CERCA_BALON_DESPACIO = 60 * (Math.PI / 180d);  // 30 grados
  public static double PASE_RIVAL_BALON_DESPACIO = 1;  // 1 m / iter
  
  
  /**
   * 
   */
  public static double MARCAJE_LIMITE_RIVALES = 15;
  public static double MARCAJE_DISTANCIA_ANTICIPACION = 5;
  public static boolean MARCAJE_TODOS_DEFENSAS = false;
  
  /**
   * 
   */
  public static double PASE_ANGULO_LARGO = 30;
  public static double PASE_ANGULO_MEDIO = 45;
  public static double PASE_ANGULO_ALTO = 60;
  public static double PASE_ANGULO_POR_DEFECTO = Constantes.ANGULO_VERTICAL;  // 30
  public static double PASE_MIN_DISTANCIA = 5;  // Un pase menos a esta distancia no ser�a �til
  public static double PASE_MAX_DISTANCIA = 40;  // Un pase a mas a esta distancia no ser�a �til
  public static double PASE_RIVAL_CERCA = 3;  
  public static int PASE_EVALUACION_MIN = 0;  
  public static int PASE_EVALUACION_LIMITE_ARCO_MEDIO = 25;  
  public static int PASE_EVALUACION_LIMITE_ARCO_LARGO = 50;  
  public static int PASE_EVALUACION_MAX = 100;  
  public static double PASE_RIVAL_ANGULO_CERCA = 30 * (Math.PI / 180d);  // 30 grados
  public static int PASE_EVALUACION_MALO = 20;
  public static int PASE_EVALUACION_MALO_AREA_PROPIA = 51;  // S�lo pases perfectos
  public static boolean PASE_MIN_DISTANCIA_PROHIBIDOS = true;
  public static boolean PASE_ATRAS_AREA_PROPIA_PROHIBIDOS = true;
  public static boolean PASE_PERMITIR_DESMARQUE = true;
  public static double PASE_LIMITE_ANGULO_DESVIACION = 70 * (Math.PI / 180d); // +/- 70�
  public static double PASE_RIVAL_ANGULO_CERCA_DESMARQUE = 15 * (Math.PI / 180d);  // 30 grados = +/- 15�

  /**
   * Regate.
   */
  public static double REGATE_LIMITE_RIVALES = 10;
  public static double REGATE_LIMITE_RIVALES_DIRECCION_DELANTE = 10;
  public static double REGATE_LIMITE_RIVALES_DIRECCION_DETRAS_LENTO = 5;
  public static double REGATE_LIMITE_ANGULO_DESVIACION = 90 * (Math.PI / 180d); // +/- 90�
  public static double REGATE_LIMITE_ANGULO_DESVIACION_UNITARIA = 15 * (Math.PI / 180d);  // 15 grados
  public static double REGATE_LIMITE_RIVALES_ANGULO_CERCA = 30 * (Math.PI / 180d);  // 30 grados
  public static double REGATE_DISTANCIA_CONTROL_BALON = 2;
  public static double REGATE_DISTANCIA_BALON_LEJOS = 8;
  public static int REGATE_LIMITE_ITERACIONES_POR_REGATE = 10;
  public static boolean REGATE_PROHIBIDO_SI_RIVAL_CERCA = true;
  public static double DISTANCIA_MAX_CORRE_RIVAL = 10 * Constantes.VELOCIDAD_MAX;
  
  /**
   * Si un jugador se encuentra a m�s de esta disatancia disparar� por alto
   */
  public static double TIRO_ALTO_LIMITE = 30;
  public static double TIRO_ANGULO_ALTO_BAJO = 12;
  public static double TIRO_ANGULO_ALTO_NORMAL = 25;
  public static boolean TIRO_EVITAR_PORTERO = true;
  public static double TIRO_EVITAR_PORTERO_METROS = 2;
  public static double TIRO_EVITAR_PORTERO_METROS_PORTERO_ALEJADO = 1;
  public static double TIRO_CERCANO_METROS_DESDE_PENALTY_5 = 5;
  public static double TIRO_CERCANO_METROS_DESDE_PENALTY_3 = 3;
//  public static double TIRO_CERCANO_METROS_DESDE_PENALTY = TIRO_CERCANO_METROS_DESDE_PENALTY_3;
  public static double TIRO_CERCANO_METROS_DESDE_PENALTY = TIRO_CERCANO_METROS_DESDE_PENALTY_5;
  public static double TIRO_CERCANO_RIVALES_CERCA_DESTINO = 4;
  public static double TIRO_CERCANO_RIVALES_CERCA_ORIGEN = 4;
//  public static double TIRO_CERCANO_RIVALES_CERCA_FACTOR = 1.2; // (un 20% de m�s)
  public static double TIRO_CERCANO_RIVALES_CERCA_FACTOR = 1.05; // (un 5% de m�s)
  
  protected static double CALCULAR_PASES_VELOCIDAD_MINIMA_APROXIMACION = 0.5; //1;
  protected static double CALCULAR_TIRO_DISTANCIA_MINIMA_APROXIMACION_XY = 0.1;
  protected static double CALCULAR_TIRO_DISTANCIA_MINIMA_APROXIMACION_Z = 0.4;

  /**
   * Tacticas
   */
  protected static final int TACTICA_NORMAL = 0;

  protected static final int TACTICA_DEFENSIVA = -1;

  protected static final int TACTICA_ATACANTE = 1;

  // ---------------------------------------------//
  // VALORES INICIALES
  // ---------------------------------------------//
  // Coordenadas por l�nea X
  public static final int X_LINEA_CENTRAL = 0;

  public static final int X_LINEA_INTERNA = 8;

  public static final int X_LINEA_MEDIO = 16;

  public static final int X_LINEA_ABIERTA = 22;

  public static final int X_LINEA_EXTERNA = 28;

  // Coordenadas por l�nea Y
  public static final int Y_LINEA_PORTERO = -52;

  public static final int Y_LINEA_AREA_META = -47;  // (int) Math.round(Constantes.centroArcoInf.getY() + Constantes.ANCHO_AREA_CHICA);

  public static final int Y_LINEA_LIBERO = -41;

  public static final int Y_LINEA_DEFENSA = -36;

  public static final int Y_LINEA_LATERALES = -28;

  public static final int Y_LINEA_MEDIO_CENTRO = -24;

  public static final int Y_LINEA_MEDIOS = -12;

  public static final int Y_LINEA_MEDIOCAMPO = 0;

  public static final int Y_LINEA_MEDIA_OFENSIVA = 5;

  public static final int Y_LINEA_MEDIAPUNTA = 15;

  public static final int Y_LINEA_DELANTEROS = 25;

  public static final int Y_LINEA_DELANTEROS_CENTRO = 37;

  // Lineas aux
  public static final double TIRO_DISTANCIA_LEJOS = 35; // Disparar desde 35 m
  public static final double TIRO_DISTANCIA_MEDIO = 25; // Disparar desde 25 m
  public static final double TIRO_DISTANCIA_CERCA = 15; // Disparar desde 15 m
  public static final double TIRO_DISTANCIA_MUY_CERCA = 8; // Disparar desde 8 m
  public static final double TIRO_DISTANCIA_NUNCA = 0; // Disparar nunca, solo desde el propio centro de la porteria rival

  // m/iter

  protected final double VALOR_INI = 0.5d;

  /**
   * Indica que no Hay ning�n jugador
   */
  public static final int NADIE = -1;
int IND_JUGADOR_0 = 10;
int IND_JUGADOR_2 = 2;
int IND_JUGADOR_10 = 0;

  /**
   * Definicion de los Cuadrantes
   */
  public static final int CUADRANTE_MAS_MAS = 1;
  public static final int CUADRANTE_MENOS_MAS = 2;
  public static final int CUADRANTE_MENOS_MENOS = 3;
  public static final int CUADRANTE_MAS_MENOS = 4;
  
  // Valores de monitorizacion 
  protected Posicion lastBalon = new Posicion(0, 0);
  protected double lastAnguloBalon = 0;
  protected double lastVelocidadBalon = 0;
//  protected double lastLastVelocidadBalon = 0;
  protected double lastAlturaBalon = 0;
  protected Vector<ComandoInfo> lastComandos = null;
  
protected Posicion lastCalcularDestinoPelota = new Posicion(0, 0);


  protected Posicion[] lastMisJugadores = new Posicion[11];
  protected Posicion[] lastRivalJugadores = new Posicion[11];
  protected int lastGolesPropios = 0;
  protected int lastGolesRival = 0;
    
  protected int lastEstimadoIteracionChute = 0;
  protected int lastEstimadoChutadorPropio = NADIE; // Si es -1 = Nadie
  protected int lastEstimadoChutadorRival = NADIE; // Si es -1 = Nadie
  protected Posicion lastBalonDestinoCalculado = null;

  
  // Valores actuales del desarrollo de la t�ctica
  protected boolean[] lastJugadoresAchicando = new boolean[11];
  protected boolean[] lastJugadoresRegateando = new boolean[11];
  protected int[] lastJugadoresRegateandoCuentaAtras = new int[11];
  
  /**
   * Incida cuando se produce un achique forzoso (es decir, no hay nadie cerca como para presionar).
   */
  protected int achicandoForzosoJugadorActual = -1;
  protected static int achicandoForzoso_LIMITE_TURNOS = 400; // = 2 segundos = 2 * 20 
  protected static int achicandoForzoso_JUGADOR_NUMERO = 8;  // Jugador 9
  protected int achicandoForzoso_turnos = 0;
  
  public static final int ANALISIS_ITERACIONES = Constantes.ITERACIONES / 6;
  protected Vector analisisMarcadorMio = new Vector();
  protected Vector analisisMarcadorRival = new Vector();
  protected Vector analisisMarcadorTirosMios = new Vector();
  protected Vector analisisMarcadorTirosRival = new Vector();
  protected int tirosTotalesMios = 0;
  protected int tirosTotalesRival = 0;
  protected static final int ANALISIS_MANTENER_ORIGINAL = 1;
  protected static final int ANALISIS_ESTUDIAR_ALTERNATIVA = 2;
  protected static final int ANALISIS_MANTENER_ALTERNATIVA = 5;
  protected int analisisActualJugadas = ANALISIS_MANTENER_ORIGINAL;
  protected int analisisActualTiros = ANALISIS_MANTENER_ORIGINAL;
  protected int analisisActualDefensa = ANALISIS_MANTENER_ORIGINAL;
    
  /**
   * JUGADAS ENSAYADAS.
   */
  protected boolean JUGADA_ENSAYADA_USAR = true;
  public static final JugadaEnsayada JUGADA_ENSAYADA_NINGUNA = null;
  protected JugadaEnsayada[] jugadasEnsayadas = new JugadaEnsayada[0];
  protected JugadaEnsayada jugadaEnsayadaActual = JUGADA_ENSAYADA_NINGUNA;
  
  public static volatile String MENSAJE = "";
  
  /**
   * Detallos de la t�ctica
   */
  protected TacticaDetalle detalle = null;  //new TacticaDetalleImpl();

    //---------------------------------------------------------------//
    public int[] addArrayToArray(int[] first, int[] last)
    {
      int[] result = new int[first.length + last.length];
      
      for (int i = 0; i < first.length; i++)
      {
        result[i] = first[i];
      }
      for (int i = 0; i < last.length; i++)
      {
        result[i + first.length] = last[i];
      }
      
      return result;
    }
    //---------------------------------------------------------------//
    public static int calcularCuadrante(Posicion posicion)
    {
      return calcularCuadrante(new Posicion(0,0), posicion);
    }
    //---------------------------------------------------------------//
    public static int calcularCuadrante(Posicion posicionIni, Posicion posicionFin)
    {
      int resultado = CUADRANTE_MAS_MAS;
      if (posicionIni.getX() >= posicionFin.getX())
      {
        if (posicionIni.getY() >= posicionFin.getY())
        {
          resultado = CUADRANTE_MAS_MAS;
        }
        else
        {
          resultado = CUADRANTE_MAS_MENOS;
        }
      }
      else
      {
        if (posicionIni.getY() >= posicionFin.getY())
        {
          resultado = CUADRANTE_MENOS_MAS;
        }
        else
        {
          resultado = CUADRANTE_MENOS_MENOS;
        }
      }
      
      return resultado;
    }
    public static boolean puedeRematar(int indJugador, SituacionPartido sp, int tipoMensaje )
    {
      boolean result = false;
      int[] indJugadores = sp.puedenRematar();
      for (int i = 0; i < indJugadores.length; i++)
      {
        if (indJugadores[i] == indJugador)
        {
          result = true;
          break;
        }
      }
      if (false == result)
      {
        // Informar sobre las iteraciones que le restan
      }
      else
      {
      }
      return result;
    }
    public static boolean puedeRegatear(int indJugador, SituacionPartido sp, int tipoMensaje, Posicion destinoRegate )
    {
      boolean result = false;
      if (puedeRematar(indJugador, sp, Termineitor.COMANDO_REGATE))
      {
        Posicion posicionJugadorMio = sp.misJugadores()[indJugador];
        int indRivalMasCercano = posicionJugadorMio.indiceMasCercano(sp.rivales());
        Posicion posicionRivalMasCercano = sp.rivales()[indRivalMasCercano];
        double distanciaJugadorMioADestino = destinoRegate.distancia(posicionJugadorMio);
        double distanciaJugadorRivalADestino = destinoRegate.distancia(posicionRivalMasCercano);
//        double limiteParaRegate = REGATE_LIMITE_RIVALES;  // valor por defecto
        double limiteParaRegate = REGATE_LIMITE_RIVALES_DIRECCION_DELANTE;  // valor por defecto
        if (distanciaJugadorMioADestino < distanciaJugadorRivalADestino)
        {
          // Mi jugador est� m�s cerca del destino que el rival
          if (sp.detalleMisJugadores()[indJugador].getVelocidad() > 
              sp.detalleJugadoresRivales()[indRivalMasCercano].getVelocidad() )
          {
            // Mi jugador es m�s r�pido que el rival
            limiteParaRegate = REGATE_LIMITE_RIVALES_DIRECCION_DETRAS_LENTO;
          }
        }
        if (posicionJugadorMio.distancia(posicionRivalMasCercano) >= limiteParaRegate)
        {
          result = true;
          
        }
        else
        {
        }
      }

      return result;
    }
    // ------------------------------------------------------------------------//
    public static double calcularDistanciaGolpeoPelota(SituacionPartido sp, int indJugador, double fuerzaGolpearBalon)
    {
      double velocidadInicial = fuerzaGolpearBalon * Constantes.getVelocidadRemate(sp.detalleMisJugadores()[indJugador].getRemate());//calcula la velocidad del remate
      return calcularDistanciaGolpeoPelota(velocidadInicial);
    }
    // ------------------------------------------------------------------------//
    public static double calcularDistanciaGolpeoPelota(double velocidadInicial)
    {
      double distancia = velocidadInicial / (1 - Constantes.FACTOR_DISMINUCION_VEL_BALON_SUELO);
      return distancia;
    }
    // ------------------------------------------------------------------------//
    public static double calcularDistanciaGolpeoPelotaIterando(double velocidadInicial, int iteraciones)
    {
      double distancia = 0;
      double velocidad = velocidadInicial;
      for (int i = 0; i < iteraciones; i++)
      {
        distancia = distancia + velocidad;
        velocidad = velocidad * Constantes.FACTOR_DISMINUCION_VEL_BALON_SUELO;
      }
      return distancia;
    }
    // ------------------------------------------------------------------------//
    public static double calcularVelocidadGolpeoPelota(double distancia, double factorRemateJugador, double anguloVertical)
    {
      double velocidadInicial = calcularVelocidadGolpeoPelota(distancia, anguloVertical);
      velocidadInicial = velocidadInicial / Constantes.getVelocidadRemate(factorRemateJugador);//calcula la velocidad del remate
      return velocidadInicial;
    }
    // ------------------------------------------------------------------------//
    public static double calcularVelocidadGolpeoPelota(double distancia, double anguloVertical)
    {
      double velocidadInicial = distancia * (1 - Constantes.FACTOR_DISMINUCION_VEL_BALON_SUELO);
//      if (anguloVertical > 0)
//        velocidadInicial = distancia * (1 - Constantes.FACTOR_DISMINUCION_VEL_BALON_AIRE) * Math.cos(anguloVertical);
      
      return velocidadInicial;
    }
    // ------------------------------------------------------------------------//
    public static double calcularVelocidadGolpeoPelotaIterando(double distanciaInicial, int iteraciones)
    {
      double frenado = 1;
      double velocidadActual = 1;
      for (int i = 0; i < iteraciones; i++)
      {
        velocidadActual = velocidadActual * Constantes.FACTOR_DISMINUCION_VEL_BALON_SUELO;
        frenado = frenado + velocidadActual;
      }
      return (distanciaInicial / (frenado));
    }
    // ------------------------------------------------------------------------//
    public Posicion calcularDestinoPelota(SituacionPartido sp)
    {
      Posicion balon = sp.balon();
      double balonDx = balon.getX() - this.lastBalon.getX();
      double balonDy = balon.getY() - this.lastBalon.getY();
      double balonDz = sp.alturaBalon() - this.lastAlturaBalon;
      double alturaBalon = sp.alturaBalon();
      
      balon = calcularDestinoPelota(balon, balonDx, balonDy, balonDz, alturaBalon);
      
this.lastCalcularDestinoPelota = balon;
     
      return balon;
    }
    // ------------------------------------------------------------------------//
    public static Posicion calcularDestinoPelota(Posicion balonIni, double balonDx, double balonDy, double balonDz, double alturaBalon)
    {
      return calcularDestinoPelota(balonIni, balonDx, balonDy, balonDz, alturaBalon, CALCULAR_PASES_VELOCIDAD_MINIMA_APROXIMACION);
    }
    // ------------------------------------------------------------------------//
    public static Posicion calcularDestinoPelota(Posicion balonIni, double balonDx, 
                                                  double balonDy, double balonDz, 
                                                  double alturaBalon, double distanciaPararDeCalcular)
    {
      Posicion balon = balonIni;
      boolean suelo = false;
     
      int iteraciones = 0;
      while ((Math.abs(balonDx) + Math.abs(balonDy)) > distanciaPararDeCalcular)
      {
        iteraciones++;
        
        if (!suelo && alturaBalon == 0 && balonDz >= 0 && balonDz < Constantes.G * 3) //condicion para que se arrastre
          suelo = true;
        
        if (suelo) 
        {
          balon = balon.moverPosicion(balonDx, balonDy);
          balonDx = balonDx * Constantes.FACTOR_DISMINUCION_VEL_BALON_SUELO;
          balonDy = balonDy * Constantes.FACTOR_DISMINUCION_VEL_BALON_SUELO;
        } 
        else 
        {
          alturaBalon = redondeaMultiplo(alturaBalon + balonDz, Constantes.G);
          balon = balon.moverPosicion(balonDx, balonDy);
          balonDz = redondeaMultiplo(balonDz - Constantes.G, Constantes.G);
          balonDx = balonDx * Constantes.FACTOR_DISMINUCION_VEL_BALON_AIRE;
          balonDy = balonDy * Constantes.FACTOR_DISMINUCION_VEL_BALON_AIRE;
          if (alturaBalon == 0) 
          {
            balonDz = (-balonDz - Constantes.G) * Constantes.FACTOR_DISMINUCION_ALTURA_BALON_REBOTE;
            balonDz = redondeaMultiplo(balonDz, Constantes.G);
          }

        }
          
        balon = balon.moverPosicion(balonDx, balonDy);
        balonDx = balonDx * Constantes.FACTOR_DISMINUCION_VEL_BALON_SUELO;
        balonDy = balonDy * Constantes.FACTOR_DISMINUCION_VEL_BALON_SUELO;
        
      }
     
      return balon;
    }
    
    // ------------------------------------------------------------------------//
    protected static double redondeaMultiplo(double valor, double divisor) 
    {
      return Math.round(valor / divisor) * divisor;
    }
    
    // ------------------------------------------------------------------------//
    protected DatosPaseJugador calcularMejorPaseJugador(int indJugador, SituacionPartido sp, int[] indJugadores, boolean permitirDesmarque)
    {
      Posicion[] misJugadores = sp.misJugadores();
      Posicion[] rivales = sp.rivales();
      double factorRemateJugador = sp.detalleMisJugadores()[indJugador].getRemate();
      
      return this.calcularMejorPaseJugador(indJugador, indJugadores, new Posicion[0], misJugadores, rivales, factorRemateJugador, permitirDesmarque);
    }
    protected DatosPaseJugador calcularMejorPaseJugador(int indJugador, 
                                                        int[] indJugadoresDestino, Posicion[] destinosPase,
                                                        Posicion[] misJugadores, Posicion[] rivales, 
                                                        double factorRemateJugador,
                                                        boolean permitirDesmarque)
    {
//      Posicion posicionMejorPase = null;
//      DatosPase datosPase = null;
//      int indJugadorMejorPase = NADIE;
      DatosPaseJugador evaluacionMejorPase = null;
      DatosPase evaluacionPase;
      
      Posicion posicionJugador;
      for (int i = 0; i < indJugadoresDestino.length; i++)
      {
        posicionJugador = misJugadores[indJugadoresDestino[i]];
        evaluacionPase = calcularEvaluacionPase(indJugador, posicionJugador, misJugadores, rivales, factorRemateJugador, permitirDesmarque);
        if ((evaluacionMejorPase == null) || (evaluacionPase.compareTo(evaluacionMejorPase) > 0))
        {
          evaluacionMejorPase = new DatosPaseJugador(indJugador, indJugadoresDestino[i], evaluacionPase);
//          posicionMejorPase = posicionJugadores[i];
//          indJugadorMejorPase = indJugadores[i];
        }
      }
      if ((destinosPase != null) && (destinosPase.length > 0))
      {
        DatosPase evaluacionPasePosiciones = calcularMejorPase(indJugador, destinosPase, misJugadores, rivales, factorRemateJugador, permitirDesmarque);
        if ((evaluacionPasePosiciones != null) && (evaluacionPasePosiciones.compareTo(evaluacionMejorPase) > 0))
        {
          int indJugadorDestino = evaluacionPasePosiciones.getDestino().indiceMasCercano(misJugadores);
          evaluacionMejorPase = new DatosPaseJugador(indJugador, indJugadorDestino, evaluacionPasePosiciones);
        }
      }
      if (evaluacionMejorPase.getEvaluacionPase() < PASE_EVALUACION_MALO)
      {
Posicion origen = misJugadores[indJugador];
Posicion destino = origen.moverPosicion(0, PASE_MAX_DISTANCIA);
int junadorCercaDestino = destino.indiceMasCercano(misJugadores, new int[]{0});
 
evaluacionMejorPase = new DatosPaseJugador(indJugador, junadorCercaDestino, origen, destino, 1, PASE_ANGULO_ALTO, -1);
      }
      
//      posicionMejorPase = sp.misJugadores()[indJugadorMejorPase];
      return evaluacionMejorPase;
    }
    
    // ------------------------------------------------------------------------//
    /**
     * Devuelve el punto del mejor pase sobre la lista de destinatarios.
     * 
     * @param indJugador jugador que quiere realizar el pase.
     * @param sp situacion del partido
     * @param indJugadores lista de destinatarios.
     * @return punto del mejor pase sobre la lista de destinatarios.
     */
    protected DatosPase calcularMejorPase(int indJugador, SituacionPartido sp, Posicion[] destinosPase , boolean permitirDesmarque)
    {
      Posicion[] misJugadores = sp.misJugadores();
      Posicion[] rivales = sp.rivales();
      double factorRemateJugador = sp.detalleMisJugadores()[indJugador].getRemate();
      return calcularMejorPase(indJugador, destinosPase, misJugadores, rivales, factorRemateJugador, permitirDesmarque );
    }
    protected DatosPase calcularMejorPase(int indJugador, Posicion[] destinosPase, 
                                          Posicion[] misJugadores, Posicion[] rivales, 
                                          double factorRemateJugador,
                                          boolean permitirDesmarque)
    {
      DatosPase evaluacionMejorPase = null;
      DatosPase evaluacionPase;
      
      for (int i = 0; i < destinosPase.length; i++)
      {
        evaluacionPase = calcularEvaluacionPase(indJugador, destinosPase[i], misJugadores, rivales, factorRemateJugador, permitirDesmarque);
        if ((evaluacionMejorPase == null) || (evaluacionPase.compareTo(evaluacionMejorPase) > 0))
        {
          evaluacionMejorPase = evaluacionPase;
        }
      }
      return evaluacionMejorPase;
    }

    //------------------------------------------------------------------------//
    protected DatosPase calcularEvaluacionPase(int indJugador, Posicion posicionDestino, 
                                                Posicion[] misJugadores, Posicion[] rivales, 
                                                double factorRemateJugador,
                                                boolean permitirDesmarque)
    {
      DatosPase datosPase = null;
      int evaluacionPase = PASE_EVALUACION_MAX; // Valor por defecto : Pase OK
      
      Posicion posicionJugadorPasa = misJugadores[indJugador];
//      Posicion posicionJugadorRecibe = sp.misJugadores()[indJugadorDestino];
      double distanciaPase = posicionJugadorPasa.distancia(posicionDestino);
      double anguloPase = posicionJugadorPasa.angulo(posicionDestino);
      if (anguloPase < 0)
        anguloPase = anguloPase + Math.PI*2;
      double minAnguloPase = anguloPase - PASE_RIVAL_ANGULO_CERCA;
//      if (minAnguloPase < 0)
//        minAnguloPase = minAnguloPase + Math.PI*2;
      double maxAnguloPase = anguloPase + PASE_RIVAL_ANGULO_CERCA;
//      if (maxAnguloPase >= Math.PI*2)
//        maxAnguloPase = maxAnguloPase - Math.PI*2;

      // Evitar los pases excesivamente cortos
      if (distanciaPase < PASE_MIN_DISTANCIA)
      {
        if (PASE_MIN_DISTANCIA_PROHIBIDOS)
          evaluacionPase = PASE_EVALUACION_MIN;  // Disminuir la valoraci�n para que no sea tenido en cuenta.
        else
          evaluacionPase = evaluacionPase / 2;  // Disminuir la valoraci�n 1/2 por cada rival
      }
      
//      boolean enAreaPropia = false;
      if (posicionJugadorPasa.getY() < Y_LINEA_DEFENSA)
      {
//        enAreaPropia = true;
        if (posicionDestino.getY() < posicionJugadorPasa.getY())
          evaluacionPase = PASE_EVALUACION_MIN;
      }
    
      double anguloPaseRival;
      Vector rivalesCercaAnguloLimitado = new Vector();
      // Comprobar si el pase no ha sido prohibido
      if (evaluacionPase > PASE_EVALUACION_MIN)
      {
        for (int i = 0; i < rivales.length; i++)
        {
          if (posicionJugadorPasa.distancia(rivales[i]) <= (distanciaPase + PASE_RIVAL_CERCA))
          {
            // Rival cercano
            anguloPaseRival = posicionJugadorPasa.angulo(rivales[i]);
//            if (anguloPaseRival < 0)
//              anguloPaseRival = anguloPaseRival + Math.PI*2;
            if ((anguloPaseRival >= minAnguloPase) && (anguloPaseRival <= maxAnguloPase))
//          if (Math.abs(anguloPaseRival - anguloPase) <= PASE_RIVAL_ANGULO_CERCA)
            {
              // Evaluacion de altura �?
              // TODO
              
              evaluacionPase = evaluacionPase / 2;  // Disminuir la valoraci�n 1/2 por cada rival
              if (permitirDesmarque)
              {
                rivalesCercaAnguloLimitado.add(new Double(anguloPaseRival));
              }
            }
            
          }
        }
      }
      // Intentar desmarque
      Posicion mejorPosicionDestino = null;
      if (permitirDesmarque)
      {
        if (rivalesCercaAnguloLimitado.size() > 0)
        {
// �aplicar un factor?
double FACTOR = 1.2; // 10 %
double distanciaPaseDesmarcando = distanciaPase * FACTOR;
          rivalesCercaAnguloLimitado.add(new Double(anguloPase + PASE_LIMITE_ANGULO_DESVIACION));
          rivalesCercaAnguloLimitado.add(new Double(anguloPase - PASE_LIMITE_ANGULO_DESVIACION));
          Collections.sort(rivalesCercaAnguloLimitado); // Ordenar los angulos rivales
          double[] gapSizes = new double[rivalesCercaAnguloLimitado.size() - 1];
          double[] gapMiddle = new double[rivalesCercaAnguloLimitado.size() - 1];
          // Calcular el tama�o del los huecos y su punto medio
          for (int i = 1; i < rivalesCercaAnguloLimitado.size(); i++)
          {
            gapSizes[i - 1] = ((Double) rivalesCercaAnguloLimitado.elementAt(i)).doubleValue() - 
                              ((Double) rivalesCercaAnguloLimitado.elementAt(i - 1)).doubleValue();
            gapMiddle[i - 1] = ((Double) rivalesCercaAnguloLimitado.elementAt(i - 1)).doubleValue() +
                               (((Double) rivalesCercaAnguloLimitado.elementAt(i)).doubleValue() - 
                                ((Double) rivalesCercaAnguloLimitado.elementAt(i - 1)).doubleValue()) / 2;
//          gapMiddle[i - 1] = (((Double) rivalesCercaAnguloLimitado.elementAt(i)).doubleValue() + 
//                              ((Double) rivalesCercaAnguloLimitado.elementAt(i - 1)).doubleValue()) / 2;
          }
          double mejorDiffGapMiddle = PASE_LIMITE_ANGULO_DESVIACION;
          int gapIndex = -1;
          double currentDiffGapMiddle;
          Posicion pos;
          for (int i = 0; i < gapSizes.length; i++)
          {
            if (gapSizes[i] > (PASE_RIVAL_ANGULO_CERCA_DESMARQUE *2 )) // Se precisa el doble del espacio libre (+/- => *2)
            {
              // Gap adecuado, �ser� el mejor?
              currentDiffGapMiddle = Math.abs(gapMiddle[i] - anguloPase);
              if (mejorDiffGapMiddle > currentDiffGapMiddle)
              {
                // Comprobar si el destino de regate esta DENTRO del campo
                pos = posicionDestino.moverAngulo( gapMiddle[i], distanciaPaseDesmarcando);
                if (pos.setDentroCampoJuego().equals(pos))
                {
                  mejorDiffGapMiddle = currentDiffGapMiddle;
                  gapIndex = i;
                }
              }
            }
          }
//double[] grados = new double[gapMiddle.length];
String[] grados = new String[gapMiddle.length];
for (int i = 0; i < grados.length; i++)
  grados[i] =  String.valueOf( (gapMiddle[i] * 180 / Math.PI) ) + "   hacia = " + posicionDestino.moverAngulo( gapMiddle[i], distanciaPaseDesmarcando) ;
          if (gapIndex >= 0)
          {
            mejorPosicionDestino = posicionJugadorPasa.moverAngulo( gapMiddle[gapIndex], distanciaPaseDesmarcando);
distanciaPase = distanciaPaseDesmarcando;
          }
          else
          {
            // No es posible realizar un buen regate
            mejorPosicionDestino = null;
          }
        }
      }
      
if (mejorPosicionDestino != null)
{
  posicionDestino = mejorPosicionDestino;
  evaluacionPase = 75;
}
//double anguloAlturaPase = PASE_ANGULO_POR_DEFECTO;
double anguloAlturaPase = PASE_ANGULO_LARGO;
if (evaluacionPase <= PASE_EVALUACION_LIMITE_ARCO_LARGO)
anguloAlturaPase = PASE_ANGULO_MEDIO; // Levantar m�s la pelota              
if (evaluacionPase <= PASE_EVALUACION_LIMITE_ARCO_MEDIO)
anguloAlturaPase = PASE_ANGULO_ALTO; // Levantar m�s la pelota              
      
double factorFuerza = calcularVelocidadGolpeoPelota( distanciaPase, factorRemateJugador, anguloAlturaPase);

      datosPase = new DatosPase(posicionJugadorPasa, posicionDestino, factorFuerza, anguloAlturaPase, evaluacionPase);

      return datosPase;
    }
    protected Posicion calcularMejorRegate(int indJugador, SituacionPartido sp, Posicion posicionDestino, int iteraciones)
    {
      Posicion mejorPosicionDestino = null;
      Posicion posicionJugadorRegatea = sp.misJugadores()[indJugador];
      Posicion[] rivales = sp.rivales();
      double distanciaReccorrida = iteraciones * Constantes.getVelocidad( sp.detalleMisJugadores()[indJugador].getVelocidad());
      
      if (puedeRematar(indJugador, sp, COMANDO_REGATE))
      {
        mejorPosicionDestino = calcularMejorRegate(indJugador, posicionJugadorRegatea, 
                                                 posicionDestino, distanciaReccorrida,
                                                 rivales);
      }
      return mejorPosicionDestino;
    }
    public static Posicion calcularMejorRegate(int indJugador, Posicion posicionJugadorRegatea, 
                                            Posicion posicionDestino, double distanciaReccorrida,
                                            Posicion[] rivales)
    {
      Posicion mejorPosicionDestino = posicionDestino;
      
      double anguloRegate = posicionJugadorRegatea.angulo(posicionDestino);
      if (anguloRegate < 0)
        anguloRegate = anguloRegate + Math.PI*2;
      double minAnguloRegate = anguloRegate - REGATE_LIMITE_RIVALES_ANGULO_CERCA;
//      if (minAnguloRegate < 0)
//        minAnguloRegate = minAnguloRegate + Math.PI*2;
      double maxAnguloRegate = anguloRegate + REGATE_LIMITE_RIVALES_ANGULO_CERCA;
//      if (maxAnguloRegate >= Math.PI*2)
//        maxAnguloRegate = maxAnguloRegate - Math.PI*2;
      double minAnguloLimitado = anguloRegate - REGATE_LIMITE_ANGULO_DESVIACION;
//      if (minAnguloLimitado < 0)
//        minAnguloLimitado = minAnguloLimitado + Math.PI*2;
      double maxAnguloLimitado = anguloRegate + REGATE_LIMITE_ANGULO_DESVIACION;
//      if (maxAnguloLimitado >= Math.PI*2)
//        maxAnguloLimitado = maxAnguloLimitado - Math.PI*2;
      
      double anguloRival;
      
//      Hashtable angulosRivalesHash = new Hashtable();
//      Integer numRivales = new Integer(0);
      Vector rivalesCercaIndice = new Vector();
//      Vector rivalesCercaAngulo = new Vector();
      // Almacenar� �nicamente los m�ngulos que est�n en el l�mite indicado
      Vector rivalesCercaAnguloLimitado = new Vector();
      boolean cancelarRegate = false;
      
      for (int i = 0; i < rivales.length; i++)
      {
        if (posicionJugadorRegatea.distancia(rivales[i]) <= (REGATE_LIMITE_RIVALES_DIRECCION_DELANTE + distanciaReccorrida))
        {
          // Rival cercano
          anguloRival = posicionJugadorRegatea.angulo(rivales[i]);
//          if (anguloRival < 0)
//            anguloRival = anguloRival + Math.PI*2;
          if (REGATE_PROHIBIDO_SI_RIVAL_CERCA && (posicionJugadorRegatea.distancia(rivales[i]) < (distanciaReccorrida /2)))
          {
              // Abortar proceso. Distancia menor que la MITAD del espacio a recorrer
              mejorPosicionDestino = null;
              cancelarRegate = true;
              break;
          }
//          if (Math.abs(anguloRival - anguloRegate) <= REGATE_LIMITE_RIVALES_ANGULO_CERCA)
          if ((anguloRival >= minAnguloRegate) && (anguloRival <= maxAnguloRegate))
          {
            if (REGATE_PROHIBIDO_SI_RIVAL_CERCA && (posicionJugadorRegatea.distancia(rivales[i]) < distanciaReccorrida))
            {
              // Abortar proceso
              mejorPosicionDestino = null;
              cancelarRegate = true;
              break;
            }
            rivalesCercaIndice.add(new Integer(i));
          }
          if ((anguloRival >= minAnguloLimitado) && (anguloRival <= maxAnguloLimitado))
          {
            rivalesCercaAnguloLimitado.add(new Double(anguloRival));
          }
        }
      }
      if (false == cancelarRegate)
      {
        // Recorrer el resto de rivales si no hay un regate limpio
        if (rivalesCercaAnguloLimitado.size() > 0)
        {
          // A�adir los limites del �rco
          rivalesCercaAnguloLimitado.add(new Double(anguloRegate + REGATE_LIMITE_ANGULO_DESVIACION));
          rivalesCercaAnguloLimitado.add(new Double(anguloRegate - REGATE_LIMITE_ANGULO_DESVIACION));
          Collections.sort(rivalesCercaAnguloLimitado); // Ordenar los angulos rivales
          // NO
//          // IMPORTANTE: NO se est�n reconvirtiendo los �ngulos, se han obtenido de 0..2*PI, luego al ser TODOS positivos
//          // se supondr� que el uso de Math.abs para el c�lculo de distancias angulares podr�a ser correcto. 
          double[] gapSizes = new double[rivalesCercaAnguloLimitado.size() - 1];
          double[] gapMiddle = new double[rivalesCercaAnguloLimitado.size() - 1];
          // Calcular el tama�o del los huecos y su punto medio
          for (int i = 1; i < rivalesCercaAnguloLimitado.size(); i++)
          {
            gapSizes[i - 1] = ((Double) rivalesCercaAnguloLimitado.elementAt(i)).doubleValue() - 
                              ((Double) rivalesCercaAnguloLimitado.elementAt(i - 1)).doubleValue();
            gapMiddle[i - 1] = ((Double) rivalesCercaAnguloLimitado.elementAt(i - 1)).doubleValue() +
                               (((Double) rivalesCercaAnguloLimitado.elementAt(i)).doubleValue() - 
                                ((Double) rivalesCercaAnguloLimitado.elementAt(i - 1)).doubleValue()) / 2;
//          gapMiddle[i - 1] = (((Double) rivalesCercaAnguloLimitado.elementAt(i)).doubleValue() + 
//                              ((Double) rivalesCercaAnguloLimitado.elementAt(i - 1)).doubleValue()) / 2;
          }
          double mejorDiffGapMiddle = REGATE_LIMITE_ANGULO_DESVIACION;
          int gapIndex = -1;
          double currentDiffGapMiddle;
          Posicion pos;
          for (int i = 0; i < gapSizes.length; i++)
          {
            if (gapSizes[i] > (REGATE_LIMITE_RIVALES_ANGULO_CERCA *2 )) // Se precisa el doble del espacio libre (+/- => *2)
            {
              // Gap adecuado, �ser� el mejor?
              currentDiffGapMiddle = Math.abs(gapMiddle[i] - anguloRegate);
              if (mejorDiffGapMiddle > currentDiffGapMiddle)
              {
                // Comprobar si el destino de regate esta DENTRO del campo
                pos = posicionJugadorRegatea.moverAngulo( gapMiddle[i], distanciaReccorrida);
                if (pos.setDentroCampoJuego().equals(pos))
                {
                  mejorDiffGapMiddle = currentDiffGapMiddle;
                  gapIndex = i;
                }
              }
            }
          }
//double[] grados = new double[gapMiddle.length];
String[] grados = new String[gapMiddle.length];
for (int i = 0; i < grados.length; i++)
  grados[i] =  String.valueOf( (gapMiddle[i] * 180 / Math.PI) ) + "   hacia = " + posicionJugadorRegatea.moverAngulo( gapMiddle[i], distanciaReccorrida) ;
          if (gapIndex >= 0)
          {
            mejorPosicionDestino = posicionJugadorRegatea.moverAngulo( gapMiddle[gapIndex], distanciaReccorrida);
          }
          else
          {
            // No es posible realizar un buen regate
            mejorPosicionDestino = null;
          }
        }
        else
        {
          // No hab�a rivales en la l�nea de regate
          mejorPosicionDestino = posicionJugadorRegatea.moverAngulo( anguloRegate, distanciaReccorrida);
        }
      }      

      return mejorPosicionDestino;
    }
    // ------------------------------------------------------------------------//
    protected static Posicion calcularAchiqueAnticipadoRival(int indJugador, Posicion destinoBalon, SituacionPartido sp, double lastVelocidadBalon)
    {

      double anguloDestinoPelota = sp.balon().angulo(destinoBalon);
      if (anguloDestinoPelota < 0)
        anguloDestinoPelota = anguloDestinoPelota + 2*Math.PI;
      
      // Calculado en funci�n de la velocidad de la pelota (quiz� tambi�n de la distancia del rival)
      double AMPLITUD_ANGULO_RIVAL_CERCA = PASE_RIVAL_ANGULO_CERCA;
      if (lastVelocidadBalon < PASE_RIVAL_BALON_DESPACIO )
        AMPLITUD_ANGULO_RIVAL_CERCA = PASE_RIVAL_ANGULO_CERCA_BALON_DESPACIO;
      
      double anguloDestinoPelotaMin = anguloDestinoPelota - AMPLITUD_ANGULO_RIVAL_CERCA;
      if (anguloDestinoPelotaMin < 0)
        anguloDestinoPelotaMin = anguloDestinoPelotaMin + 2*Math.PI;
      double anguloDestinoPelotaMax = anguloDestinoPelota + AMPLITUD_ANGULO_RIVAL_CERCA;
      if (anguloDestinoPelotaMax > 2*Math.PI)
        anguloDestinoPelotaMin = anguloDestinoPelotaMax - 2*Math.PI;
      
      Posicion posCercanoBalon = sp.misJugadores()[indJugador];
      double distanciaMioBalon = sp.balon().distancia(posCercanoBalon);
      
      int[] rivalesCercaPelota = new int[11];
      double anguloRivalBalon;
      double distanciaRivalBalon;
      for (int i = 0; i < rivalesCercaPelota.length; i++)
      {
        rivalesCercaPelota[i] = -1;
        distanciaRivalBalon = sp.balon().distancia(sp.rivales()[i]);
        if ((distanciaMioBalon) > (distanciaRivalBalon - ACHIQUE_ANTICIPADO_RIVALES_LIMITE))
        {
          if (distanciaRivalBalon < ACHIQUE_ANTICIPADO_RIVALES_CERCA_PELOTA)
          {
            anguloRivalBalon = sp.balon().angulo(sp.rivales()[i]);
            if (anguloRivalBalon < 0)
              anguloRivalBalon = anguloRivalBalon + 2*Math.PI;
            if ((anguloRivalBalon >= anguloDestinoPelotaMin) && (anguloRivalBalon <= anguloDestinoPelotaMax))
            {
              rivalesCercaPelota[i] = i;
              
// Rival m�s cerca que el destino de la pelota (recortar achique)
double ventaja = distanciaMioBalon - distanciaRivalBalon + ACHIQUE_ANTICIPADO_RIVALES_LIMITE;

destinoBalon = destinoBalon.moverAngulo( destinoBalon.angulo(sp.balon()), ventaja);

              break;
            }
          }
        }
      }

      return destinoBalon;
    }
    
    //------------------------------------------------------------------------//
    public static LinkedList<Comando> addComandosToList(Vector<ComandoInfo> comandos, 
                                              LinkedList<Comando> listaDestino,
                                              boolean eliminarPrimerosGolpeosBalonMismoJugador)
    {
      ComandoInfo comandoInfo;
      Comando comando;
      
      boolean[] jugadoresChutadores = new boolean[11];
      for (int i = 0; i < jugadoresChutadores.length; i++)
        jugadoresChutadores[i] = false;

      for (int i =  comandos.size(); i > 0; i--)
      {
        comandoInfo = (ComandoInfo) comandos.elementAt(i - 1);
        comando = comandoInfo.getComando();
        if (comando.getTipo().equals(Tipo.GOLPEAR_BOLA))
        {
          if (false == jugadoresChutadores[comando.getIndJugador()])
          {
            jugadoresChutadores[comando.getIndJugador()] = true;
            listaDestino.addFirst(comando);
          }
          else
          {
            if (false == eliminarPrimerosGolpeosBalonMismoJugador)
              listaDestino.addFirst(comando);
          }
        }
        else
          listaDestino.addFirst(comando);          
      }

      return listaDestino;
    }
    
    
    protected class TacticaDetalleImpl implements TacticaDetalle
      {
        public String getNombre()
        {
          return "TERMINEITOR";
        }

        public String getPais()
        {
          return "España";
        }

        public String getEntrenador()
        {
//if (this.tacticaTermineitor.jugadaEnsayadaActual != null)
//  return this.tacticaTermineitor.jugadaEnsayadaActual.getNombreJugada();
          return "DANIEL PEIRED";
        }

        public Color getColorCamiseta()
        {
          return new Color(204, 204, 0);
        }

        public Color getColorPantalon()
        {
          return new Color(0, 0, 0);
        }

        public Color getColorFranja()
        {
          return new Color(0, 0, 0);
        }

        public Color getColorCalcetas()
        {
          return new Color(204, 204, 0);
        }

        public Color getColorPortero()
        {
          return new Color(153, 51, 0);
        }

        public EstiloUniforme getEstilo()
        {
          return EstiloUniforme.LINEAS_VERTICALES;
        }

        public Color getColorCamiseta2()
        {
          return new Color(44, 109, 47);
        }

        public Color getColorPantalon2()
        {
          return new Color(148, 150, 34);
        }

        public Color getColorFranja2()
        {
          return new Color(40, 131, 47);
        }

        public Color getColorCalcetas2()
        {
          return new Color(63, 227, 73);
        }

        public Color getColorPortero2()
        {
          return new Color(123, 204, 104);
        }

        public EstiloUniforme getEstilo2()
        {
          return EstiloUniforme.FRANJA_VERTICAL;
        }

        class JugadorImpl implements JugadorDetalle
          {

            String nombre;

            int numero;

            Color piel, pelo;

            double velocidad, remate, presicion;

            boolean portero;

            Posicion posicion;

            // ---------------------------------------------------------------//
            /**
             * 
             * @param nombre
             * @param numero
             * @param piel
             * @param pelo
             * @param velocidad
             *          0..1, 0 = 0.3 m/it .. 0.5 = 0.45 m/it .. 1 = 0.6 m/it
             * @param remate
             *          0..1, 0 = 1.2 m/it .. 0.5 = 1.8 m/it .. 1 = 2.4 m/it
             * @param presicion
             *          0..1, 0 = 25 % .. 0.5 = 15 % .. 1 = 5 %
             * @param portero
             */
            public JugadorImpl(String nombre, int numero, Color piel, Color pelo, double velocidad, double remate, double presicion, boolean portero)
            {
              this.nombre = nombre;
              this.numero = numero;
              this.piel = piel;
              this.pelo = pelo;
              this.velocidad = velocidad;
              this.remate = remate;
              this.presicion = presicion;
              this.portero = portero;
            }

            public String getNombre()
            {
              return nombre;
            }

            public Color getColorPiel()
            {
              return piel;
            }

            public Color getColorPelo()
            {
              return pelo;
            }

            public int getNumero()
            {
              return numero;
            }

            public boolean esPortero()
            {
              return portero;
            }

            public double getVelocidad()
            {
              return velocidad;
            }

            public double getRemate()
            {
              return remate;
            }

            public double getPresicion()
            {
              return presicion;
            }

          }

        /**
         * VALORES POR DEFECTO
         */

        // ---------------------------------------------------------------//
        /**
         * 
         */
        public String getNombreDefault(int indJugador)
        {
          String nombre = "";

          if (indJugador == 0)
            nombre = "PORTERO";
          else if (indJugador == 1)
            nombre = "DEFENSA_DCHO";
          else if (indJugador == 2)
            nombre = "DEFENSA_IZDO";
          else if (indJugador == 3)
            nombre = "LATERAL_DCHO";
          else if (indJugador == 4)
            nombre = "LATERAL_IZDO";
          else if (indJugador == 5)
            nombre = "MEDIO_CENTRO";
          else if (indJugador == 6)
            nombre = "MEDIO_IZDO";
          else if (indJugador == 7)
            nombre = "MEDIO_DCHO";
          else if (indJugador == 8)
            nombre = "PUNTA_DCHO";
          else if (indJugador == 9)
            nombre = "PUNTA_IZDO";
          else if (indJugador == 10)
            nombre = "DELANTERO";

          return nombre;
        }

        // ---------------------------------------------------------------//
        /**
         * Esta funci�n retorna el color de la piel del jugador.
         * 
         * @param indJugador
         *          indice del jugador [0..10]
         * @return el color de la piel del jugador.
         */
        public Color getColorPielDefault(int indJugador)
        {
          Color colorPiel = new Color(255, 200, 150);

          // El mediocentro ser� m�s oscuro :-D
          if (indJugador == 5)
            colorPiel = colorPiel.darker();

          return colorPiel;
        }

        // ---------------------------------------------------------------//
        /**
         * 
         * @param indJugador
         * @return
         */
        public Color getColorPeloDefault(int indJugador)
        {
          Color colorPelo = new Color(50, 0, 0);

          if (indJugador == 0)
            colorPelo = new Color(255, 255, 0);

          return colorPelo;
        }

        // ---------------------------------------------------------------//
        /**
         * 
         * @param indJugador
         * @return
         */
        public int getNumeroDefault(int indJugador)
        {
          return indJugador + 1;
        }

        // ---------------------------------------------------------------//
        /**
         * 
         * @param indJugador
         * @return
         */
        public boolean esPorteroDefault(int indJugador)
        {
          boolean esPortero = false;
          if (indJugador == 0)
            esPortero = true;

          return esPortero;
        }

        // ---------------------------------------------------------------//
        /**
         * 
         * @param indJugador
         * @return
         */
        public double getVelocidadDefault(int indJugador)
        {
          return VALOR_INI;
        }

        // ---------------------------------------------------------------//
        /**
         * 
         * @param indJugador
         * @return
         */
        public double getRemateDefault(int indJugador)
        {
          return VALOR_INI;
        }

        // ---------------------------------------------------------------//
        /**
         * 
         * @param indJugador
         * @return
         */
        public double getPresicionDefault(int indJugador)
        {
          return VALOR_INI;
        }

        // ---------------------------------------------------------------//
        /**
         * 
         * @param indJugador
         * @return
         */
        public JugadorDetalle[] getJugadores()
        {
          JugadorDetalle[] jugadores = new JugadorDetalle[11];
          jugadores[IND_JUGADOR_0] = new JugadorImpl("ROMA",      1, new Color(255, 200, 150), new Color(255, 255, 0), 1.0d,  0.55d,  1.0d,  true);
          jugadores[1] = new JugadorImpl("EDUARDO", 2, new Color(255, 200, 150), new Color(255, 255, 0), 1.0d,  0.70d, 0.75d, false);
          jugadores[IND_JUGADOR_2] = new JugadorImpl("MARIO", 3, new Color(255, 200, 150), new Color(50, 0, 0),    0.75d, 0.5d,  0.75d, false);
          jugadores[3] = new JugadorImpl("VICTOR", 4, new Color(255, 200, 150), new Color(50, 0, 0),    0.75d, 0.5d,  0.75d, false);
          jugadores[4] = new JugadorImpl("FEDE", 5, new Color(255, 200, 150), new Color(50, 0, 0),    0.75d, 0.75d, 0.75d, false);
          jugadores[5] = new JugadorImpl("BENITO", 6, new Color(255, 200, 150), new Color(255, 255, 0), 1.0d,  0.5d,  1.0d,  false);
          jugadores[6] = new JugadorImpl("RAUL",   7, new Color(255, 200, 150), new Color(255, 255, 0), 1.0d,  0.75d, 1.0d,  false);
          jugadores[7] = new JugadorImpl("DANI",   8, new Color(255, 200, 150), new Color(255, 255, 0), 1.0d,  0.75d, 1.0d,  false);
          jugadores[8] = new JugadorImpl("FERNANDO",   9, new Color(190, 150, 100), new Color(255, 255, 0), 1.0d,  1.0d,  0.75d, false);
          jugadores[9] = new JugadorImpl("JULI",  10, new Color(190, 150, 100), new Color(255, 255, 0), 1.0d,  1.0d,  1.0d,  false);
          jugadores[IND_JUGADOR_10]= new JugadorImpl("SERGIO",   11, new Color(190, 150, 100), new Color(50, 0, 0),    0.75d, 1.0d,  0.75d, false);

          // Jugadores 1 : velocidad => Rubios
          // Jugadores 1 : fuerza => Negros
          
          // Ejemplo de datos:
          // 0.75 de precision => 10% de error equivale 180*0.1  = 18 grados = +/- 9   grados
          // 1.00 de precision =>  5% de error equivale 180*0.05 =  9 grados = +/- 4.5 grados
          
          // 1 partido = 3 min = 180 segs = 3600 iteraciones = 20 iter / segundo
          
          // JugadorDetalle[] jugadores = new JugadorDetalle[11];
          // for (int i=0; i<11; i++)
          // {
          // jugadores[i] = new JugadorImpl( getNombreDefault(i),
          // getNumeroDefault(i),
          // getColorPielDefault(i),
          // getColorPeloDefault(i),
          // getVelocidadDefault(i),
          // getRemateDefault(i),
          // getPresicionDefault(i),
          // esPorteroDefault(i));
          // System.err.println("JUGADOR = "+i+" .- "+jugadores[i]);
          // }
          return jugadores;
        }
      }


  //---------------------------------------------------------------//
  //    CONSTRUCTOR    //
  //---------------------------------------------------------------//
    
  //---------------------------------------------------------------//
  /**
   * 
   *
   */

    public static void main(String[] args) throws Exception {
        
        Termineitor.class.getConstructor(new Class[]{}).newInstance(new Object[]{});
    }

  public Termineitor()
  {
    this.detalle = new TacticaDetalleImpl();
    
    this.jugadasEnsayadas = this.crearJugadasEnsayadas(); 
    
    for (int i = 0; i < this.lastJugadoresRegateando.length; i++)
    {
      this.lastJugadoresAchicando[i] = false;
      this.lastJugadoresRegateando[i] = false;
    }
  }
  public TacticaDetalle getDetalle()
  {
    return detalle;
  }

  public Posicion[] getPosicionSaca(SituacionPartido sp)
  {
//    return alineacion5;
    Posicion[] posicionIniciales = getPosicionInicial(sp);
//    double distanciaCentro;
    for (int i = 0; i < posicionIniciales.length; i++)
    {
      // Ajustar los jugadores en campo propio
      if (posicionIniciales[i].getY() > 0)
      {
        posicionIniciales[i] = new Posicion( posicionIniciales[i].getX(), 0 );
      }
    }
    posicionIniciales[IND_JUGADOR_10] = new Posicion( Constantes.centroCampoJuego );
    return posicionIniciales;
  }

  public Posicion[] getPosicionRecive(SituacionPartido sp)
  {
//    return alineacion6;
    Posicion[] posicionIniciales = getPosicionInicial(sp);
    double distanciaCentro;
    for (int i = 0; i < posicionIniciales.length; i++)
    {
      // Ajustar los jugadores en campo propio
      if (posicionIniciales[i].getY() > 0)
      {
        posicionIniciales[i] = new Posicion( posicionIniciales[i].getX(), -1 );
        distanciaCentro = Constantes.centroCampoJuego.distancia(posicionIniciales[i]);
        if (distanciaCentro < Constantes.DISTANCIA_SAQUE)
        {
          posicionIniciales[i] = posicionIniciales[i].moverPosicion( 0, (distanciaCentro - Constantes.DISTANCIA_SAQUE));
        }
      }
    }
    return posicionIniciales;
  }

  //---------------------------------------------------------------//
  /**
   * 
   * @param sp
   * @return
   */
  public int getTactica(SituacionPartido sp)
  {
    int tacticaUtilizada = TACTICA_NORMAL;
    return tacticaUtilizada;
  }

  //---------------------------------------------//
  // M�TODOS AUXILIARES (vac�os)
  //---------------------------------------------//

  //---------------------------------------------//
  protected int[] getPortero(SituacionPartido sp, int tacticaUtilizada)
  {
    // Tactica por defecto
    int[] indJugadores = new int[1];
    indJugadores[0] = IND_JUGADOR_0;
    
    return indJugadores;
  }
  //---------------------------------------------//
  protected int[] getDefensas(SituacionPartido sp, int tacticaUtilizada)
  {
    // Tactica por defecto
    int[] indJugadores = new int[4];
    indJugadores[0] = 1;
    indJugadores[1] = IND_JUGADOR_2;
    indJugadores[2] = 3;
    indJugadores[3] = 4;
    
    return indJugadores;
  }
  //---------------------------------------------//
  protected int[] getLiberos(SituacionPartido sp, int tacticaUtilizada)
  {
    // Tactica por defecto
    int[] indJugadores = new int[0];
    
    return indJugadores;
  }  
  //---------------------------------------------//  
  protected int[] getMedios(SituacionPartido sp, int tacticaUtilizada)
  {
    // Tactica por defecto
    int[] indJugadores = new int[4];
    indJugadores[0] = 5;
    indJugadores[1] = 6;
    indJugadores[2] = 7;
    indJugadores[3] = 8;
    
    return indJugadores;
  }
  //---------------------------------------------//
  protected int[] getMedioCentros(SituacionPartido sp, int tacticaUtilizada)
  {
    // Tactica por defecto
    int[] indJugadores = new int[0];
    
    return indJugadores;
  }
  //---------------------------------------------//
  protected int[] getMediaPuntas(SituacionPartido sp, int tacticaUtilizada)
  {
    // Tactica por defecto
    int[] indJugadores = new int[0];
    
    return indJugadores;
  }
  //---------------------------------------------//
  protected int[] getDelanteros(SituacionPartido sp, int tacticaUtilizada)
  {
    // Tactica por defecto
    int[] indJugadores = new int[2];
    indJugadores[0] = 9;
    indJugadores[1] = IND_JUGADOR_10;

    return indJugadores;
  }
  protected int[] getDelanteroCentro(SituacionPartido sp, int tacticaUtilizada)
  {
    // Tactica por defecto
    int[] indJugadores = new int[0];
    
    return indJugadores;
  }
  public Posicion[] getPosicionInicialPorteros(SituacionPartido sp, int tacticaUtilizada)
  {
    // Tactica por defecto
    Posicion[] posiciones = new Posicion[1];

    posiciones[0] = new Posicion(X_LINEA_CENTRAL, Y_LINEA_PORTERO);

    return posiciones;
  }
  public Posicion[] getPosicionInicialDefensas(SituacionPartido sp, int tacticaUtilizada)
  {
    // Tactica por defecto
    Posicion[] posiciones = new Posicion[4];

    posiciones[0] = new Posicion(X_LINEA_INTERNA, Y_LINEA_DEFENSA); // 2
    posiciones[1] = new Posicion(-X_LINEA_INTERNA, Y_LINEA_DEFENSA); // 3
    posiciones[2] = new Posicion(X_LINEA_ABIERTA, Y_LINEA_LATERALES); // 4
    posiciones[3] = new Posicion(-X_LINEA_ABIERTA, Y_LINEA_LATERALES); // 5

    return posiciones;
  }
  public Posicion[] getPosicionInicialMedios(SituacionPartido sp, int tacticaUtilizada)
  {
    // Tactica por defecto
    Posicion[] posiciones = new Posicion[4];

    posiciones[0] = new Posicion(X_LINEA_CENTRAL, Y_LINEA_MEDIO_CENTRO); // 6
    posiciones[1] = new Posicion(-X_LINEA_ABIERTA, Y_LINEA_MEDIOS); // 7
    posiciones[2] = new Posicion(X_LINEA_MEDIO, Y_LINEA_MEDIOS); // 8
    posiciones[3] = new Posicion(-X_LINEA_INTERNA, Y_LINEA_MEDIA_OFENSIVA); // 9
    // (media
    // punta)

    return posiciones;
  }
  public Posicion[] getPosicionInicialAtacantes(SituacionPartido sp, int tacticaUtilizada)
  {
    // Tactica por defecto
    Posicion[] posiciones = new Posicion[2];

    posiciones[0] = new Posicion(X_LINEA_MEDIO, ((Y_LINEA_DELANTEROS + Y_LINEA_MEDIAPUNTA)/2)); // 10
    posiciones[1] = new Posicion(X_LINEA_CENTRAL, Y_LINEA_DELANTEROS_CENTRO); // 11

    return posiciones;
  }
  public Posicion[] getPosicionInicial(SituacionPartido sp)
  {
    int tacticaUtilizada = getTactica(sp);

    Posicion[] posicionPorteros = getPosicionInicialPorteros(sp, tacticaUtilizada);
    Posicion[] posicionDefensas = getPosicionInicialDefensas(sp, tacticaUtilizada);
    Posicion[] posicionMedios = getPosicionInicialMedios(sp, tacticaUtilizada);
    Posicion[] posicionAtacantes = getPosicionInicialAtacantes(sp, tacticaUtilizada);

    Posicion[] posiciones = new Posicion[11];
    int[] indices;
    indices = getPortero(sp, tacticaUtilizada);
    for (int i = 0; i < posicionPorteros.length; i++)
    {
      posiciones[indices[i]] = posicionPorteros[i];
    }
    indices = getDefensas(sp, tacticaUtilizada);
    for (int i = 0; i < posicionDefensas.length; i++)
    {
//      posiciones[i + posicionPorteros.length] = posicionDefensas[i];
      posiciones[indices[i]] = posicionDefensas[i];
    }
    indices = getMedios(sp, tacticaUtilizada);
    for (int i = 0; i < posicionMedios.length; i++)
    {
//      posiciones[i + posicionPorteros.length + posicionDefensas.length] = posicionMedios[i];
      posiciones[indices[i]] = posicionMedios[i];
    }
    indices = getDelanteros(sp, tacticaUtilizada);
    for (int i = 0; i < posicionAtacantes.length; i++)
    {
//      posiciones[i + posicionPorteros.length + posicionDefensas.length + posicionMedios.length] = posicionAtacantes[i];
      posiciones[indices[i]] = posicionAtacantes[i];
    }

    return posiciones;
  }

  //---------------------------------------------//
  /**
   * Retorna los movimientos iniciales.
   */
//  protected Vector<ComandoInfo> movimientoInicial(SituacionPartido sp)
//  {
//    Vector<ComandoInfo> comandos = new Vector<ComandoInfo>();
//
//    Posicion[] posicionesIniciales = getPosicionInicial(sp);
//
//    String info;
//    for (int n = 0; n < 11; n++)
//    {
//      info = "movimientoInicial desde = "+sp.misJugadores()[n];
//      if (SHOW_COMANDOS_INICIAL_RIVALES)
//        info = info + "\t\t Rival ("+n+") esta en : " + sp.rivales()[n];
//      comandos.add(new ComandoInfo(new ComandoIrA(n, posicionesIniciales[n]), info ));
//    }
//    return comandos;
//  }
  protected Vector<ComandoInfo> movimientoInicial(SituacionPartido sp)
  {
    return movimientoInicialMejorado(sp);
  }

  //---------------------------------------------//
  /**
   * Retorna los movimientos iniciales.
   */
  protected Vector<ComandoInfo> movimientoInicialMejorado(SituacionPartido sp)
  {
    Vector<ComandoInfo> comandos = new Vector<ComandoInfo>();

    Posicion[] posicionesIniciales = getPosicionInicial(sp);
    
    Posicion[] posicionesRivales = sp.rivales();
    Posicion[] posicionesJugadores = sp.misJugadores();
    int indRivalCercano;

    Posicion posicionDestino;
    String info;
    for (int n = 0; n < 11; n++)
    {
      posicionDestino = posicionesIniciales[n];
      indRivalCercano = posicionesIniciales[n].indiceMasCercano(posicionesRivales);
      info = "movimientoInicial desde = "+sp.misJugadores()[n];
      if (posicionesRivales[indRivalCercano].distancia(posicionDestino) < MOVIMIENTO_DESMARQUE_RIVAL_CERCA)
      {
        posicionDestino = posicionDestino.moverAngulo( posicionDestino.angulo(sp.balon()), MOVIMIENTO_DESMARQUE_LIMITE);
        info = info + " <Desviado> ";
      }
      if (SHOW_COMANDOS_INICIAL_RIVALES)
        info = info + "\t\t Rival ("+n+") esta en : " + sp.rivales()[n];
      
      comandos.add(new ComandoInfo(new ComandoIrA(n, posicionDestino), info));
    }
    return comandos;
  }

  //---------------------------------------------//
  /**
   * Retorna los movimientos del portero.
   */
  protected Vector<ComandoInfo> movimientoPortero( SituacionPartido sp)
  {
//    Vector<ComandoInfo> comandos = new Vector<ComandoInfo>();
//    return comandos;
    
    Vector<ComandoInfo> comandos = new Vector<ComandoInfo>();
    
    Posicion posicion;
    double limiteAchique = ACHIQUE_LIMITE_PORTERO;
    boolean bascular = true;  // valor por defecto
    
    // Detectar disparo a puerta
    if ((this.lastBalonDestinoCalculado != null) && (this.lastBalonDestinoCalculado.getY() <= Constantes.centroArcoInf.getY()))
    {
      posicion = Posicion.Interseccion( sp.balon(), this.lastBalonDestinoCalculado, 
                                         new Posicion(Constantes.posteIzqArcoInf.getX(), Y_LINEA_PORTERO), 
                                         new Posicion(Constantes.posteDerArcoInf.getX(), Y_LINEA_PORTERO)) ;
      
      comandos.add( new ComandoInfo(new ComandoIrA(0, posicion), "movimientoPortero evitar GOL , desde = "+sp.misJugadores()[0]));
      bascular = false;
    }
    else if (sp.balon().distancia(getPosicionInicial(sp)[IND_JUGADOR_0]) < limiteAchique)
    {
      if ((sp.alturaBalon() <= Constantes.ALTURA_CONTROL_BALON) || ((sp.alturaBalon() <= this.lastAlturaBalon)))
      {
        comandos.add( new ComandoInfo( new ComandoIrA(IND_JUGADOR_0, sp.balon()), "movimientoAchique del Portero") );
        this.lastJugadoresAchicando[IND_JUGADOR_0] = true;
        bascular = false;
      }
    }
    if (bascular)
    {
      // Bascular seg�n la direcci�n de la pelota 
      
double LIMITE_DEL_POSTE = 2;  // Distancia l�mite a la que bascular� m�s all� del poste
  
      // Tactica Test. El portero bascula 1/4 de la pelota (30m => 7.5m)
      double posicionX = sp.balon().getX() / 4;
      if (posicionX > (Constantes.posteDerArcoInf.getX() + LIMITE_DEL_POSTE))
      {
        posicionX = Constantes.posteDerArcoInf.getX() + LIMITE_DEL_POSTE;
      }
      else if (posicionX < (Constantes.posteIzqArcoInf.getX() - LIMITE_DEL_POSTE))
      {
        posicionX = Constantes.posteIzqArcoInf.getX() - LIMITE_DEL_POSTE;
      }
      
      posicion = new Posicion( posicionX, Y_LINEA_PORTERO);
  
      comandos.add( new ComandoInfo(new ComandoIrA(IND_JUGADOR_0, posicion), "movimientoPortero"));
    }
    return comandos;
  }
  protected Vector<ComandoInfo> movimientoDefensa( SituacionPartido sp)
  {
    Vector<ComandoInfo> comandos = new Vector<ComandoInfo>();
    
    // Si la pelota se encuentra en el �rea ajustar las posiciones
    Posicion balon = sp.balon();
    if (balon.getY() < Y_LINEA_DEFENSA)
    {
      int indDefensa = 1; // Central derecho
      Posicion posDestino = new Posicion( X_LINEA_INTERNA, Y_LINEA_AREA_META);
      comandos.add(new ComandoInfo(new ComandoIrA(indDefensa, posDestino), "movimientoDefensa balon peligroso"));
      indDefensa = 2; // Central izquierdo
      posDestino = new Posicion( - X_LINEA_INTERNA, Y_LINEA_AREA_META);
      comandos.add(new ComandoInfo(new ComandoIrA(indDefensa, posDestino), "movimientoDefensa balon peligroso"));
      indDefensa = 3; // Lateral derecho
      posDestino = new Posicion( X_LINEA_MEDIO, Y_LINEA_LIBERO);
      comandos.add(new ComandoInfo(new ComandoIrA(indDefensa, posDestino), "movimientoDefensa balon peligroso"));
      indDefensa = 4; // Lateral izquierdo
      posDestino = new Posicion( - X_LINEA_MEDIO, Y_LINEA_LIBERO);
      comandos.add(new ComandoInfo(new ComandoIrA(indDefensa, posDestino), "movimientoDefensa balon peligroso"));
    }
//    else if (balon.getY() > Y_LINEA_MEDIA_OFENSIVA)
    else if (balon.getY() > Y_LINEA_MEDIOS)
    {
      
      // Marcaje por delante
//      int tactica = getTactica(sp);
//      Posicion[] posicionesIniciales = this.getPosicionInicialDefensas(sp, tactica);
      Posicion[] posicionesIniciales = this.getPosicionInicial(sp);
      
//      int[] defensas = this.getDefensas(sp, tactica);
// Por el momento s�lo realizar�n el marcaje los latereales
int[] defensas = new int[] {3, 4};
if (MARCAJE_TODOS_DEFENSAS)
  defensas = this.getDefensas(sp, getTactica(sp));
      // 
      int[] indicesRivalCercano;
      int[] rivalesMarcados = new int[11];
      for (int i = 0; i < 11; i++)
        rivalesMarcados[i] = 0; // portero
      Vector<ComandoInfo> comandosMarcaje;
      for (int i = 0; i < defensas.length; i++)
      {
//        int indRivalCercano = sp.misJugadores()[defensas[i]].indiceMasCercano(sp.rivales());
        indicesRivalCercano = posicionesIniciales[defensas[i]].indicesMasCercanos(sp.rivales(), rivalesMarcados);
        for (int j = 0; j < rivalesMarcados.length; j++)
        {
          if (sp.rivales()[indicesRivalCercano[j]].distancia( posicionesIniciales[defensas[i]] ) <= MARCAJE_LIMITE_RIVALES)
          {
            rivalesMarcados[indicesRivalCercano[j]] = indicesRivalCercano[j];
            comandosMarcaje = this.crearComandoMarcaje(sp, defensas[i], indicesRivalCercano[j]);
            comandos.addAll(comandosMarcaje);
            break;
          }
        }
      }
      
    }
    
    return comandos;
  }
  protected Vector<ComandoInfo> movimientoMedia( SituacionPartido sp)
  {
    Vector<ComandoInfo> comandos = new Vector<ComandoInfo>();
    
double LIMITE_DE_ALEJAMIENTO = 10;  // Distancia l�mite a la que bascular� m�s all� de la posicion inicial

boolean bascularMedios = true;

    int tactica = this.getTactica(sp);
    int[] indMedios = this.getMedios(sp, tactica);
    Posicion[] posicionesIniciales = this.getPosicionInicialMedios(sp, tactica);

    // Adelantar posicion cuando se ataca
    if (sp.balon().getY() > Y_LINEA_MEDIAPUNTA)
    {
      // Bal�n en zona de ataque, retroceder un poco la posici�n de los puntas
      // NOTA: Este comando podr�a imposibilitar que los medios defiendan en el achique (por el momento el achique sobreescribir�a estos comandos)
      for (int i = 0; i < indMedios.length; i++)
      {
//        comandos.add( new ComandoIrA(indAtacantes[i], posicionesIniciales[i].moverPosicion(0, (Y_LINEA_DELANTEROS - Y_LINEA_DELANTEROS_CENTRO)) ) );
        comandos.add( new ComandoInfo(new ComandoIrA(indMedios[i], 
                                                      posicionesIniciales[i].moverPosicion(0, MOVIMIENTO_MEDIOS_ATADELANTE) ),
                                      "Mediocampista Avanza en Y"));
      }
    }
    // Retrasar posicion cuando se defiende
    if (sp.balon().getY() < Y_LINEA_MEDIO_CENTRO)
    {
      // Bal�n en zona de defensa, retroceder un poco la posici�n de los puntas
      // NOTA: Este comando podr�a imposibilitar que los medios defiendan en el achique (por el momento el achique sobreescribir�a estos comandos)
      for (int i = 0; i < indMedios.length; i++)
      {
        comandos.add( new ComandoInfo(new ComandoIrA(indMedios[i], 
                                                      posicionesIniciales[i].moverPosicion(0, MOVIMIENTO_MEDIOS_ATRAS) ),
                                      "Mediocampista Retrocede en Y"));
      }
    }

    return comandos;
  }
  protected Vector<ComandoInfo> movimientoAtacantes( SituacionPartido sp)
  {
    Vector<ComandoInfo> comandos = new Vector<ComandoInfo>();
    
    // Ajustar la posicion horizontal X
    
double LIMITE_DE_ALEJAMIENTO = 10;  // Distancia l�mite a la que bascular� m�s all� de la posicion inicial

boolean bascularDelanteros = true;

    // Tactica Test. Bascular 1/4 de la pelota (30m => 7.5m)
    double posicionX = sp.balon().getX() / 4;
    if (bascularDelanteros)
    {
      int tactica = this.getTactica(sp);
      int[] indAtacantes = this.getDelanteros(sp, tactica);
      Posicion[] posicionesIniciales = this.getPosicionInicialAtacantes(sp, tactica);
      for (int i = 0; i < indAtacantes.length; i++)
      {
        if (sp.misJugadores()[indAtacantes[i]].distancia(posicionesIniciales[i]) < LIMITE_DE_ALEJAMIENTO)
          comandos.add( new ComandoInfo(new ComandoIrA(indAtacantes[i], sp.misJugadores()[indAtacantes[i]].moverPosicion(posicionX, 0) ), "Delantero Bascula en X") );
//        comandos.add( new ComandoIrA(indAtacantes[i], posicionesIniciales[i].moverPosicion(posicionX, 0) ) );
      }
    }
    
    // Retrasar posicion cuando se defiende
    if (sp.balon().getY() < Y_LINEA_MEDIOS)
    {
      // Bal�n en zona de defensa, retroceder un poco la posici�n de los puntas
      int tactica = this.getTactica(sp);
      int[] indAtacantes = this.getDelanteros(sp, tactica);
      Posicion[] posicionesIniciales = this.getPosicionInicialAtacantes(sp, tactica);
      // NOTA: Este comando podr�a imposibilitar que los delanteros defiendan en el achique (por el momento el achique sobreescribir�a estos comandos)
      for (int i = 0; i < indAtacantes.length; i++)
      {
//        comandos.add( new ComandoIrA(indAtacantes[i], posicionesIniciales[i].moverPosicion(0, (Y_LINEA_DELANTEROS - Y_LINEA_DELANTEROS_CENTRO)) ) );
        comandos.add( new ComandoInfo(new ComandoIrA(indAtacantes[i], 
                                                      posicionesIniciales[i].moverPosicion(posicionX, MOVIMIENTO_ATACANTES_ATRAS) ),
                                      "Delantero Retrocede en Y"));
      }
    }
    
    return comandos;
  }
  protected Vector<ComandoInfo> movimientoAchique( SituacionPartido sp)
  {
//    Vector<ComandoInfo> comandos = new Vector<ComandoInfo>();
//    return comandos;

    Vector<ComandoInfo> comandos = new Vector<ComandoInfo>();
    
    Posicion balon = sp.balon();
    int[] indJugadoresOrdenados =  balon.indicesMasCercanos(sp.misJugadores());
    int indJugador = NADIE;
    double limiteAchique;
//    boolean achicaPortero = false;

//double ACHIQUE_LIMITE = 20;
Posicion[] posicionesIniciales = getPosicionInicial(sp);

    Vector jugadoresAchicadores = new Vector();
    for (int i = 0; i < indJugadoresOrdenados.length; i++)
    {
//      achicaPortero = false;
      
      limiteAchique = ACHIQUE_LIMITE;
      if (indJugadoresOrdenados[i] == IND_JUGADOR_0)
        limiteAchique = ACHIQUE_LIMITE_PORTERO;
      
      if (balon.distancia(posicionesIniciales[indJugadoresOrdenados[i]]) < limiteAchique)
      {
        if (indJugadoresOrdenados[i] == IND_JUGADOR_0)
        {
          // El Portero SIEMPRE deber� intentar ir a por la pelota si esta baja O est� bajando
          if ((sp.alturaBalon() <= Constantes.ALTURA_CONTROL_BALON) || ((sp.alturaBalon() <= this.lastAlturaBalon)))
          {
            comandos.add( new ComandoInfo( new ComandoIrA(indJugadoresOrdenados[i], balon), "movimientoAchique del Portero") );
            this.lastJugadoresAchicando[indJugadoresOrdenados[i]] = true;
//            achicaPortero = true;
          }
        }
        else
        {
          // Excepto el portero, el resto de jugadores deben de ser anotados en la lista
          // (para poder seleccionarlos bien por distancia al bal�n, bien por su dicersidad de direcci�n)
          jugadoresAchicadores.addElement(Integer.valueOf(indJugadoresOrdenados[i]));
        }
      }
      else
      {
        this.lastJugadoresAchicando[indJugadoresOrdenados[i]] = false;
      }
    }
    // int indJugador;
    
    // Achique Forzoso �?
    if (ACHIQUE_FORZOSO)
    {
      if (jugadoresAchicadores.size() == 0)
      {
        this.achicandoForzosoJugadorActual = achicandoForzoso_JUGADOR_NUMERO;
        this.achicandoForzoso_turnos = 0;
        
      }
      this.achicandoForzoso_turnos ++;
      if (this.achicandoForzoso_turnos >= achicandoForzoso_LIMITE_TURNOS)
      {
        // Abortar achique forzoso
        this.achicandoForzosoJugadorActual = NADIE;
        this.achicandoForzoso_turnos = 0;
      }
      if (this.achicandoForzosoJugadorActual != NADIE)
      {
        jugadoresAchicadores.addElement(Integer.valueOf(this.achicandoForzosoJugadorActual));
      }
    }
    
    String infoAchiqueForzoso = "";
    int cuadrante = 0;
    int[] achicandoresPorCuadrante = new int[] {0,0,0,0,0};  // Cuadrantes 1..4
    Posicion destinoAchique;
    String descripcionAchique;
    for (int i = 0; i < jugadoresAchicadores.size(); i++)
    {
      indJugador = ((Integer) jugadoresAchicadores.elementAt(i)).intValue();
        
      if (ACHIQUE_POR_CUADRANTES)
      {
        cuadrante = calcularCuadrante(balon, sp.misJugadores()[indJugador]);
        achicandoresPorCuadrante[cuadrante]++;
      }
      if (achicandoresPorCuadrante[cuadrante] > 1)
      {
      }
      else
      {
        
        if (comandos.size() < ACHIQUE_LIMITE_JUGADORES) 
        {
          
          // Tactica Test. El Jugador m�s cercano al bal�n lo persigue siempre y cuando no se aleje 20m 
if (indJugador == this.achicandoForzosoJugadorActual)
  infoAchiqueForzoso = " FORZOSO (turno " + this.achicandoForzoso_turnos + ")";
else
  infoAchiqueForzoso = "";

          // Achique mejorado si la distanci es grande, anticipar destino del bal�n
descripcionAchique = "movimientoAchique";
destinoAchique = balon;
if (sp.misJugadores()[indJugador].distancia(balon) > REGATE_LIMITE_RIVALES_DIRECCION_DETRAS_LENTO)
{
descripcionAchique = "movimientoAchique Avanzado";
  destinoAchique = balon.moverAngulo(this.lastAnguloBalon, this.lastVelocidadBalon);
}

          comandos.add( new ComandoInfo( new ComandoIrA(indJugador, destinoAchique), descripcionAchique + infoAchiqueForzoso) );
          this.lastJugadoresAchicando[indJugador] = true;
        }
        else
        {
          this.lastJugadoresAchicando[indJugador] = false;
        }
      }
        
    }
    
    // Enviar al jugador m�s cercano al destino del pase a por la pelota.
    if (ACHIQUE_ANTICIPADO)
    {
      boolean velocidadMinimaOK = true;
      if (this.lastVelocidadBalon < ACHIQUE_ANTICIPADO_VELOCIDAD_MINIMA)
      {
        velocidadMinimaOK = false;
      }
      if ((this.lastBalonDestinoCalculado != null) && (velocidadMinimaOK))
      {
        Posicion destinoBalon = this.lastBalonDestinoCalculado;
        if (false == destinoBalon.setDentroCampoJuego().equals(destinoBalon))
        {
          // Bal�n fuera => Intersectar si es disparo a puerta rival
          if (destinoBalon.getY() < Constantes.centroArcoInf.getY())
          {
            destinoBalon = Posicion.Interseccion( sp.balon(), this.lastBalonDestinoCalculado, 
                                           new Posicion(Constantes.posteIzqArcoInf.getX(), Y_LINEA_PORTERO), 
                                           new Posicion(Constantes.posteDerArcoInf.getX(), Y_LINEA_PORTERO)) ;
          }
          else
          {
            // No buscar un bal�n fuera del campo
            destinoBalon = null;
          }
        }
        if (destinoBalon != null)
        {
          int indCercano = destinoBalon.indiceMasCercano(sp.misJugadores());
          
          
          if (ACHIQUE_ANTICIPADO_RIVALES)
          {
            // Metodo a usar SIEMPRE, no solo si la pelota va rapida
            destinoBalon = calcularAchiqueAnticipadoRival( indCercano, destinoBalon,
                                                           sp, this.lastVelocidadBalon);
          }
          
          comandos.add( new ComandoInfo( new ComandoIrA(indCercano, destinoBalon), "Achique Anticipado para el jugador "+indCercano+" (achicaba="+this.lastJugadoresAchicando[indCercano]+") destino estimado "+this.lastBalonDestinoCalculado) );
        }
      }
      else
      {
        if (ACHIQUE_ANTICIPADO_RIVALES)
        {
          Posicion destinoBalon = sp.balon();
          int indCercano = destinoBalon.indiceMasCercano(sp.misJugadores());
          // Metodo a usar SIEMPRE, no solo si la pelota va rapida
          destinoBalon = calcularAchiqueAnticipadoRival( indCercano, destinoBalon,
                                                         sp, this.lastVelocidadBalon);
          comandos.add( new ComandoInfo( new ComandoIrA(indCercano, destinoBalon), "Achique Anticipado con balon lento para el jugador "+indCercano+" (achicaba="+this.lastJugadoresAchicando[indCercano]+") destino estimado "+this.lastBalonDestinoCalculado) );
        }
        
      }

    }
    
    return comandos;
  }
  protected Vector<ComandoInfo> golpeoBalonPaseDefecto( SituacionPartido sp)
  {
    Vector<ComandoInfo> comandos = new Vector<ComandoInfo>();
    return comandos;
  }
  protected Vector<ComandoInfo> golpeoBalonPaseMejorado( SituacionPartido sp)
  {
    Vector<ComandoInfo> comandos = new Vector<ComandoInfo>();
    
    // Tactica Test.
    Posicion[] misJugadores = sp.misJugadores();
    int indJugador;
    DatosPase datosPase;
//    Posicion destinoPase;
//    double anguloPase;
//    double fuerzaPase;
    boolean permitirDesmarque = PASE_PERMITIR_DESMARQUE;
    boolean esConDesmarque;
    Posicion[] destinosPosiblesPase;
    
    boolean pelotaEnAreaPropia = false;
    String mensajePelotaEnAreaPropia = ""; 
    if (sp.balon().getY() < Y_LINEA_DEFENSA)
    {
      pelotaEnAreaPropia = true;
      mensajePelotaEnAreaPropia = " la Pelota esta en el Area Propia "; 
    }
    
    // Jugador 0 (1 : portero), pasar� a los jugadores (3=4, 4=5)
    // -----------------------
    indJugador = IND_JUGADOR_0;
    if (Termineitor.puedeRematar(indJugador, sp, Termineitor.COMANDO_PASE))
    {
      destinosPosiblesPase = new Posicion[4];
      if (sp.balon().getX() > this.lastBalon.getX())
      {
        // El bal�n va desde la izquierda a la derecha (despejar m�s a la derecha = 4 (3))
        destinosPosiblesPase[0] = misJugadores[3];
        destinosPosiblesPase[1] = new Posicion( X_LINEA_EXTERNA, Y_LINEA_DEFENSA);
        destinosPosiblesPase[2] = misJugadores[4];
        destinosPosiblesPase[3] = new Posicion( - X_LINEA_EXTERNA, Y_LINEA_DEFENSA);
      }
      else
      {
        // El bal�n va desde la derecha a la izquierda (despejar m�s a la izquierda)
        destinosPosiblesPase[0] = misJugadores[4];
        destinosPosiblesPase[1] = new Posicion( - X_LINEA_EXTERNA, Y_LINEA_DEFENSA);
        destinosPosiblesPase[2] = misJugadores[3];
        destinosPosiblesPase[3] = new Posicion( X_LINEA_EXTERNA, Y_LINEA_DEFENSA);
      }
      esConDesmarque = false; // En caso de duda se efectuar� un pase en largo
      datosPase = calcularMejorPase(indJugador, sp, destinosPosiblesPase, esConDesmarque);
      if (pelotaEnAreaPropia)
      {
        if (datosPase.getEvaluacionPase() < PASE_EVALUACION_MALO_AREA_PROPIA)
          datosPase = new DatosPase(misJugadores[indJugador], misJugadores[indJugador].moverPosicion(0, PASE_MAX_DISTANCIA), 1.0, PASE_ANGULO_MEDIO, -1);
      }
      
//    anguloPase = datosPase.getAnguloVertical();
//    comandos.add(new ComandoGolpearBalon(0, destinoPase, fuerza, anguloPase));
      comandos.add(new ComandoInfo( new ComandoGolpearBalon(indJugador, datosPase.getDestino(), datosPase.getFactorFuerza(), datosPase.getAnguloVertical()),
                                    "Pase Mejorado (evaluacion = " + datosPase.getEvaluacionPase() + ")"));
    }
    
    // Jugador 1 (2 : defensa derecho), pasar� a los jugadores (7=8, 3=4, 5=6)
    // -----------------------
    indJugador = 1;
    if (Termineitor.puedeRematar(indJugador, sp, Termineitor.COMANDO_PASE))
    {
      if (pelotaEnAreaPropia)
      {
        destinosPosiblesPase = new Posicion[4];
        destinosPosiblesPase[0] = misJugadores[7];
        destinosPosiblesPase[1] = misJugadores[3];
        destinosPosiblesPase[2] = misJugadores[5];
        destinosPosiblesPase[3] = new Posicion( X_LINEA_EXTERNA, Y_LINEA_MEDIOS);
        esConDesmarque = false; // En caso de duda se efectuar� un pase en largo
        datosPase = calcularMejorPase(indJugador, sp, destinosPosiblesPase, esConDesmarque);
        
        if (datosPase.getEvaluacionPase() < PASE_EVALUACION_MALO_AREA_PROPIA)
          datosPase = new DatosPase(misJugadores[indJugador], misJugadores[indJugador].moverPosicion(0, PASE_MAX_DISTANCIA), 1.0, PASE_ANGULO_MEDIO, -1);
      }
      else
      {
        esConDesmarque = permitirDesmarque;
        datosPase = calcularMejorPaseJugador(indJugador, sp, new int[] {7, 3, 5}, esConDesmarque);
      }
      comandos.add(new ComandoInfo( new ComandoGolpearBalon(indJugador, datosPase.getDestino(), datosPase.getFactorFuerza(), datosPase.getAnguloVertical()),
                                    "Pase Mejorado"+mensajePelotaEnAreaPropia+" (evaluacion = " + datosPase.getEvaluacionPase() + ")"));
    }
    
    // Jugador 2 (3 : defensa izquierdo), pasar� a los jugadores (4=5, 5=6)
    // -----------------------
    indJugador = IND_JUGADOR_2;
    if (Termineitor.puedeRematar(indJugador, sp, Termineitor.COMANDO_PASE))
    {
      if (pelotaEnAreaPropia)
      {
        destinosPosiblesPase = new Posicion[3];
//      destinosPosiblesPase = new Posicion[2];
        destinosPosiblesPase[0] = misJugadores[4];
        destinosPosiblesPase[1] = misJugadores[5];
        destinosPosiblesPase[2] = new Posicion( - X_LINEA_EXTERNA, Y_LINEA_MEDIOS);
        esConDesmarque = false; // En caso de duda se efectuar� un pase en largo
        datosPase = calcularMejorPase(indJugador, sp, destinosPosiblesPase, esConDesmarque);
        
        if (datosPase.getEvaluacionPase() < PASE_EVALUACION_MALO_AREA_PROPIA)
          datosPase = new DatosPase(misJugadores[indJugador], misJugadores[indJugador].moverPosicion(0, PASE_MAX_DISTANCIA), 1.0, PASE_ANGULO_MEDIO, -1);
      }
      else
      {
        esConDesmarque = permitirDesmarque;
        datosPase = calcularMejorPaseJugador(indJugador, sp, new int[] {4, 5}, esConDesmarque);
      }
      comandos.add(new ComandoInfo( new ComandoGolpearBalon(indJugador, datosPase.getDestino(), datosPase.getFactorFuerza(), datosPase.getAnguloVertical()),
                                    "Pase Mejorado"+mensajePelotaEnAreaPropia+" (evaluacion = " + datosPase.getEvaluacionPase() + ")"));
    }
    
    // Jugador 3 (4 : lateral derecho), pasar� a los jugadores (7=8, 5=6)
    // -----------------------
    indJugador = 3;
    if (Termineitor.puedeRematar(indJugador, sp, Termineitor.COMANDO_PASE))
    {
      if (pelotaEnAreaPropia)
      {
        destinosPosiblesPase = new Posicion[3];
//      destinosPosiblesPase = new Posicion[2];
        destinosPosiblesPase[0] = misJugadores[7];
        destinosPosiblesPase[1] = misJugadores[5];
        destinosPosiblesPase[2] = new Posicion( X_LINEA_EXTERNA, Y_LINEA_MEDIA_OFENSIVA);
        esConDesmarque = false; // En caso de duda se efectuar� un pase en largo
        datosPase = calcularMejorPase(indJugador, sp, destinosPosiblesPase, esConDesmarque);
        
        if (datosPase.getEvaluacionPase() < PASE_EVALUACION_MALO_AREA_PROPIA)
          datosPase = new DatosPase(misJugadores[indJugador], misJugadores[indJugador].moverPosicion(0, PASE_MAX_DISTANCIA), 1.0, PASE_ANGULO_MEDIO, -1);
      }
      else
      {
        esConDesmarque = permitirDesmarque;
        datosPase = calcularMejorPaseJugador(indJugador, sp, new int[] {7, 5}, esConDesmarque);
      }
      comandos.add(new ComandoInfo( new ComandoGolpearBalon(indJugador, datosPase.getDestino(), datosPase.getFactorFuerza(), datosPase.getAnguloVertical()),
                                    "Pase Mejorado"+mensajePelotaEnAreaPropia+" (evaluacion = " + datosPase.getEvaluacionPase() + ")"));
    }
    
    // Jugador 4 (5 : lateral izquierdo), pasar� a los jugadores (6=7, 5=6)
    // -----------------------
    indJugador = 4;
    if (Termineitor.puedeRematar(indJugador, sp, Termineitor.COMANDO_PASE))
    {
      if (pelotaEnAreaPropia)
      {
        destinosPosiblesPase = new Posicion[3];
//      destinosPosiblesPase = new Posicion[2];
        destinosPosiblesPase[0] = misJugadores[6];
        destinosPosiblesPase[1] = misJugadores[5];
        destinosPosiblesPase[2] = new Posicion( - X_LINEA_EXTERNA, Y_LINEA_MEDIA_OFENSIVA);
        esConDesmarque = false; // En caso de duda se efectuar� un pase en largo
        datosPase = calcularMejorPase(indJugador, sp, destinosPosiblesPase, esConDesmarque);
        
        if (datosPase.getEvaluacionPase() < PASE_EVALUACION_MALO_AREA_PROPIA)
          datosPase = new DatosPase(misJugadores[indJugador], misJugadores[indJugador].moverPosicion(0, PASE_MAX_DISTANCIA), 1.0, PASE_ANGULO_MEDIO, -1);
      }
      else
      {
        esConDesmarque = permitirDesmarque;
        datosPase = calcularMejorPaseJugador(indJugador, sp, new int[] {6, 5}, esConDesmarque);
      }
      comandos.add(new ComandoInfo( new ComandoGolpearBalon(indJugador, datosPase.getDestino(), datosPase.getFactorFuerza(), datosPase.getAnguloVertical()),
                                    "Pase Mejorado"+mensajePelotaEnAreaPropia+" (evaluacion = " + datosPase.getEvaluacionPase() + ")"));
    }
    
    // Jugador 5 (6 : medio centro), pasar� a los jugadores (6=7, 7=8, 8=9)
    // -----------------------
    indJugador = 5;
    if (Termineitor.puedeRematar(indJugador, sp, Termineitor.COMANDO_PASE))
    {
      esConDesmarque = permitirDesmarque;
      datosPase = calcularMejorPaseJugador(indJugador, sp, new int[] {6, 7, 8}, esConDesmarque);
        
      if ((pelotaEnAreaPropia) && (datosPase.getEvaluacionPase() < PASE_EVALUACION_MALO_AREA_PROPIA))
        datosPase = new DatosPase(misJugadores[indJugador], misJugadores[indJugador].moverPosicion(0, PASE_MAX_DISTANCIA), 1.0, PASE_ANGULO_MEDIO, -1);
        
      comandos.add(new ComandoInfo( new ComandoGolpearBalon(indJugador, datosPase.getDestino(), datosPase.getFactorFuerza(), datosPase.getAnguloVertical()),
                                  "Pase Mejorado"));
    }
    
    // Jugador 6 (7 : interior izquierdo), pasar� a los jugadores (8=9, 10=11)
    // -----------------------
    indJugador = 6;
    if (Termineitor.puedeRematar(indJugador, sp, Termineitor.COMANDO_PASE))
    {
      esConDesmarque = permitirDesmarque;
      datosPase = calcularMejorPaseJugador(indJugador, sp, new int[] {8, IND_JUGADOR_10}, esConDesmarque);
      comandos.add(new ComandoInfo( new ComandoGolpearBalon(indJugador, datosPase.getDestino(), datosPase.getFactorFuerza(), datosPase.getAnguloVertical()),
                                  "Pase Mejorado"));
    }
    
    // Jugador 7 (8 : interior derecho), pasar� a los jugadores (9=10, 8=9)
    // -----------------------
    indJugador = 7;
    if (Termineitor.puedeRematar(indJugador, sp, Termineitor.COMANDO_PASE))
    {
      esConDesmarque = permitirDesmarque;
      datosPase = calcularMejorPaseJugador(indJugador, sp, new int[] {9, 8}, esConDesmarque);
      comandos.add(new ComandoInfo( new ComandoGolpearBalon(indJugador, datosPase.getDestino(), datosPase.getFactorFuerza(), datosPase.getAnguloVertical()),
                                  "Pase Mejorado"));
    }
    
    // Jugador 8 (9 : media punta), pasar� a los jugadores (10=11, 9=10)
    // -----------------------
    indJugador = 8;
    if (Termineitor.puedeRematar(indJugador, sp, Termineitor.COMANDO_PASE))
    {
      esConDesmarque = permitirDesmarque;
      datosPase = calcularMejorPaseJugador(indJugador, sp, new int[] {IND_JUGADOR_10, 9}, esConDesmarque);
      comandos.add(new ComandoInfo( new ComandoGolpearBalon(indJugador, datosPase.getDestino(), datosPase.getFactorFuerza(), datosPase.getAnguloVertical()),
                                  "Pase Mejorado"));
    }
    
    // Jugador 9 (10 : delantero derecho), pasar� a los jugadores (10=11)
    // -----------------------
    indJugador = 9;
    if (Termineitor.puedeRematar(indJugador, sp, Termineitor.COMANDO_PASE))
    {
      esConDesmarque = permitirDesmarque;
      datosPase = calcularMejorPaseJugador(indJugador, sp, new int[] {IND_JUGADOR_10}, esConDesmarque);
      comandos.add(new ComandoInfo( new ComandoGolpearBalon(indJugador, datosPase.getDestino(), datosPase.getFactorFuerza(), datosPase.getAnguloVertical()),
                                    "Pase Mejorado"));
    }
    
    return comandos;
  }

  protected Vector<ComandoInfo> golpeoBalonRegate( SituacionPartido sp)
  {
    Vector<ComandoInfo> comandos = new Vector<ComandoInfo>();
    
//// DANI TEMPORAL NO REGATEAR    
double DISTANCIA_RECORRIDA_BALON = 20;
double DISTANCIA_CONTROL_BALON = 2;


    int[] medioCentros = this.getMedioCentros(sp, this.getTactica(sp));
    int[] medios = this.getMedios(sp, this.getTactica(sp));
    int[] mediaPuntas = this.getMediaPuntas(sp, this.getTactica(sp));
    int[] regateadores = medioCentros;
    regateadores = addArrayToArray(regateadores, medios);
    regateadores = addArrayToArray(regateadores, mediaPuntas);

    Posicion posicionJugadorMio;
    Posicion destinoRegate;
    Posicion destinoMejorRegate;
// Decisi�n sobre el destino de un regate
Posicion posMedia;
double REGATE_LIMITE_RIVALES_POS_MEDIA = REGATE_LIMITE_RIVALES * 2; 
Posicion[] misJugadores = sp.misJugadores();
Posicion[] rivales = sp.rivales();
int rivalesCercaParaCentrar = 0;
int jugadoresCercaParaRematar = 0;
int rivalesCercaParaTirar = 0;
for (int i = 0; i < misJugadores.length; i++)
{
  if (Constantes.penalSup.distancia(misJugadores[i]) <= REGATE_LIMITE_RIVALES)
    jugadoresCercaParaRematar++;
}
rivalesCercaParaCentrar = 0;
for (int j = 0; j < rivales.length; j++)
{
  if (Constantes.penalSup.distancia(rivales[j]) <= REGATE_LIMITE_RIVALES)
    rivalesCercaParaCentrar++;
}


    Vector<ComandoInfo> comandosRegate;
    int indJugador;
    for (int i = 0; i < regateadores.length; i++)
    {
      indJugador = regateadores[i];
      this.lastJugadoresRegateando[indJugador] = false;
      posicionJugadorMio = sp.misJugadores()[indJugador];
      destinoRegate = posicionJugadorMio.moverPosicion(0, DISTANCIA_RECORRIDA_BALON);
      
      if (puedeRematar(indJugador, sp, COMANDO_REGATE))
      {
        // Comprobar si el destino de regate es muy escorado
        if (destinoRegate.getY() > Y_LINEA_DELANTEROS_CENTRO)
        {
          if ( Math.abs( destinoRegate.getX() ) >= X_LINEA_ABIERTA)
          {
            // Centrar la posicion o centrar
            boolean centrar = false;
            if (jugadoresCercaParaRematar > 0)
            {
              posMedia = Posicion.media( posicionJugadorMio, Constantes.centroArcoSup);
              rivalesCercaParaTirar = 0;
              for (int j = 0; j < rivales.length; j++)
              {
                if (posMedia.distancia(rivales[j]) <= REGATE_LIMITE_RIVALES_POS_MEDIA)
                  rivalesCercaParaTirar++;
              }
              if ((jugadoresCercaParaRematar - rivalesCercaParaCentrar) > rivalesCercaParaTirar)
                centrar = true;
            }
            if (centrar)
            {
destinoRegate = new Posicion( posicionJugadorMio.getX(), Constantes.penalSup.getY() + 5);
            }
            else
            {
destinoRegate = posicionJugadorMio.moverAngulo( posicionJugadorMio.angulo(Constantes.centroArcoSup), DISTANCIA_RECORRIDA_BALON);
            }
          }
        }
        
        // Calcular mejor regate
        destinoMejorRegate = calcularMejorRegate(regateadores[i], sp, destinoRegate, REGATE_LIMITE_ITERACIONES_POR_REGATE);
        
//        if (puedeRegatear( indJugador, sp, COMANDO_JUGADA, destinoRegate))
        if (null != destinoMejorRegate)
        {
            double factorRemateJugador = sp.detalleMisJugadores()[indJugador].getRemate();
            
            comandosRegate = this.crearComandoRegate( indJugador, 
                                                      destinoRegate,
                                                      posicionJugadorMio,
                                                      factorRemateJugador);
            comandos.addAll(comandosRegate);
            this.lastJugadoresRegateando[indJugador] = true;
        }
      }
      
    }
    
    return comandos;
  }

  protected Vector<ComandoInfo> golpeoBalonTiro( SituacionPartido sp)
  {
    Vector<ComandoInfo> comandos = new Vector<ComandoInfo>();
    
double fuerza = 1;

    Posicion balon = sp.balon();

    Posicion destinoDisparo = Constantes.centroArcoSup;
    double distanciaBalonPorteria = destinoDisparo.distancia( balon );
    
    if (distanciaBalonPorteria <= TIRO_DISTANCIA_LEJOS)
    {
      
      double evitarPorteroDesdeLejos = 0;
      double evitarPorteroDesdeCerca = 0;
      double evitarPorteroFactor = 1;
      if (TIRO_EVITAR_PORTERO)
      {
        int indPortero;
        Posicion portero = Constantes.centroArcoSup;
        for (int i = 0; i < sp.rivales().length; i++)
        {
          if (sp.detalleJugadoresRivales()[i].esPortero())
          {
            indPortero = i;
            portero = sp.rivales()[i];
          }
        }
//        int indDefensaPortero = destinoDisparo.indiceMasCercano(sp.rivales());
//        Posicion defensaPortero = sp.rivales()[indDefensaPortero];
        
        evitarPorteroDesdeLejos = TIRO_EVITAR_PORTERO_METROS;
//        evitarPorteroDesdeCerca = TIRO_EVITAR_PORTERO_METROS + 1;
        if (portero.getX() >= 0)
        {
          evitarPorteroDesdeLejos = - TIRO_EVITAR_PORTERO_METROS;
//          evitarPorteroDesdeCerca = - TIRO_EVITAR_PORTERO_METROS - 1;
        }
        
        if ((Math.abs(destinoDisparo.getX() - portero.getX())) > TIRO_EVITAR_PORTERO_METROS_PORTERO_ALEJADO)
//        if (destinoDisparo.distancia(portero) > TIRO_EVITAR_PORTERO_METROS_PORTERO_ALEJADO)
        {
          evitarPorteroFactor = 0.5;
          
//System.out.println("TacticaTest.golpeoBalonTiro() TIRO_EVITAR_PORTERO_METROS_PORTERO_ALEJADO = "+destinoDisparo.distancia(portero)+ "  X = " +(Math.abs(destinoDisparo.getX() - portero.getX())) + "\t PORTERO = "+portero+ "\t evitarPortero="+evitarPortero);        
        }
// en funcion de la distancia a puerta /2 � /4      
//        destinoDisparo = destinoDisparo.moverPosicion( evitarPorteroDesdeLejos, 0);
      }
      evitarPorteroDesdeCerca = evitarPorteroDesdeLejos;
      evitarPorteroDesdeLejos = evitarPorteroDesdeLejos * evitarPorteroFactor;

      
double anguloVertical = TIRO_ANGULO_ALTO_NORMAL;
      if (balon.distancia(destinoDisparo) < TIRO_ALTO_LIMITE)
        anguloVertical = TIRO_ANGULO_ALTO_BAJO;

      Posicion posJugador;
      int indJugador;
//      int[] medios = this.getMedios(sp, this.getTactica(sp));
      int[] medioCentros = this.getMedioCentros(sp, this.getTactica(sp));
      int[] medios = this.getMedios(sp, this.getTactica(sp));
      int[] mediaPuntas = this.getMediaPuntas(sp, this.getTactica(sp));
      int[] tiradores = medioCentros;
      tiradores = addArrayToArray(tiradores, medios);
      tiradores = addArrayToArray(tiradores, mediaPuntas);
      Vector<ComandoInfo> comandoTiroCercano;
      double desviacionParaEvitarPortero;
      for (int i = 0; i < tiradores.length; i++)
      {
        indJugador = tiradores[i];
        posJugador = sp.misJugadores()[indJugador];
        if (posJugador.distancia(destinoDisparo) <= TIRO_DISTANCIA_CERCA)
        {
          if (Termineitor.puedeRematar(indJugador, sp, Termineitor.COMANDO_TIRO))
          {
            desviacionParaEvitarPortero = evitarPorteroDesdeCerca;
            
            comandoTiroCercano = crearComandoTiroCercano(indJugador, destinoDisparo.moverPosicion( evitarPorteroDesdeCerca, 0), balon, sp.misJugadores(), sp.rivales(), sp.detalleMisJugadores()[indJugador].getRemate());
            comandos.addAll(comandoTiroCercano);
//            comandos.add(new ComandoInfo( new ComandoGolpearBalon(indJugador, destinoDisparo, fuerza, anguloVertical), "Tiro mejorado medios"));
          }
        }
      }

      indJugador = 9;
      if (Termineitor.puedeRematar(indJugador, sp, Termineitor.COMANDO_TIRO))
      {
        desviacionParaEvitarPortero = evitarPorteroDesdeCerca;
        if (sp.misJugadores()[indJugador].distancia(destinoDisparo) > TIRO_DISTANCIA_CERCA)
          desviacionParaEvitarPortero = evitarPorteroDesdeLejos;
            
        comandoTiroCercano = crearComandoTiroCercano(indJugador, destinoDisparo.moverPosicion( desviacionParaEvitarPortero, 0), balon, sp.misJugadores(), sp.rivales(), sp.detalleMisJugadores()[indJugador].getRemate());
        comandos.addAll(comandoTiroCercano);
//        comandos.add(new ComandoInfo( new ComandoGolpearBalon(indJugador, destinoDisparo, fuerza, anguloVertical), "Tiro mejorado delantero"));
      }
      
      indJugador = IND_JUGADOR_10;
      if (Termineitor.puedeRematar(indJugador, sp, Termineitor.COMANDO_TIRO))
      {
        desviacionParaEvitarPortero = evitarPorteroDesdeCerca;
        if (sp.misJugadores()[indJugador].distancia(destinoDisparo) > TIRO_DISTANCIA_CERCA)
          desviacionParaEvitarPortero = evitarPorteroDesdeLejos;
        
        comandoTiroCercano = crearComandoTiroCercano(indJugador, destinoDisparo.moverPosicion( desviacionParaEvitarPortero, 0), balon, sp.misJugadores(), sp.rivales(), sp.detalleMisJugadores()[indJugador].getRemate());
        comandos.addAll(comandoTiroCercano);
//        comandos.add(new ComandoInfo( new ComandoGolpearBalon(indJugador, destinoDisparo, fuerza, anguloVertical), "Tiro mejorado delantero"));
      }

    }
      
    
    
    return comandos;
  }
  

  //---------------------------------------------//
  /**
   * Retorna los comandos cuando hay un saque.
   */
  protected Vector<ComandoInfo> getComandosSaque( SituacionPartido sp)
  {
//    Vector<Comando> comandos = new Vector<Comando>();
//    return comandos;
    
    Vector<ComandoInfo> comandos = new Vector<ComandoInfo>();
    
    // Identificar el tipo de saque
double CERCA = 2;   // 2 metros

    if (sp.saco() || (sp.iteracion() == 0))
    {
      Posicion posPosteDerecho = Constantes.posteDerArcoInf;
      Posicion saqueMetaDerecha = posPosteDerecho.moverPosicion(Constantes.ANCHO_AREA_CHICA, Constantes.ANCHO_AREA_CHICA);
      Posicion posPosteIzquierdo = Constantes.posteIzqArcoInf;
      Posicion saqueMetaIzquierda = posPosteIzquierdo.moverPosicion(- Constantes.ANCHO_AREA_CHICA, Constantes.ANCHO_AREA_CHICA);
      
      // Saque de Centro
      if (sp.balon().distancia(Constantes.centroCampoJuego) <= CERCA)
      {
        comandos = this.getComandosSaqueCentro(sp);
      }
      // Saque de Esquina
      else if ((Constantes.cornerSupDer.distancia(sp.balon()) <= CERCA) ||
          (Constantes.cornerSupIzq.distancia(sp.balon()) <= CERCA))
      {
        comandos = this.getComandosSaqueEsquina(sp);
      }
      // Saque de Banda 
      else if (((sp.balon().getX() - CERCA) <= Constantes.cornerInfIzq.getX()) ||
               ((sp.balon().getX() + CERCA) >= Constantes.cornerInfDer.getX()))
      {
        comandos = this.getComandosSaqueBanda(sp);
      }
      // Saque de Puerta 
      else if ((saqueMetaDerecha.distancia(sp.balon()) <= CERCA) ||
               (saqueMetaIzquierda.distancia(sp.balon()) <= CERCA))
      {
        comandos = this.getComandosSaqueMeta(sp);
      }
      else
      {
System.err.println("NO S� qu� saque es :-( ");        
      }
    }
    else if ((sp.sacaRival()))
    {
      
    }

    
    return comandos;
  }

  protected Vector<ComandoInfo> getComandosSaqueCentro( SituacionPartido sp)
  {
    Vector<ComandoInfo> comandos = new Vector<ComandoInfo>();
    
    // El Destino del Saque de Centro es el jugador del MedioCentro (6= 5)
    int indJugadorDestinoDelPase = 5;
    Posicion destinoDelSaque = sp.misJugadores()[indJugadorDestinoDelPase];
    double distanciaDelPase = destinoDelSaque.distancia(sp.balon());
// Valor inicial de fuerza = 1      
//      double fuerzaDelSaque = 1.0;
    
    // Tactica Test. Los delanteros pasan hacia atr�s
    int indJugadorSaca = 9;
    if (Termineitor.puedeRematar(indJugadorSaca, sp, Termineitor.COMANDO_SAQUE))
    {
double factorRemateJugador = sp.detalleMisJugadores()[indJugadorSaca].getRemate();
double anguloVertical = PASE_ANGULO_POR_DEFECTO;
      double fuerzaDelSaque = calcularVelocidadGolpeoPelota(distanciaDelPase, factorRemateJugador, anguloVertical);
      comandos.add(new ComandoInfo( new ComandoGolpearBalon(indJugadorSaca, destinoDelSaque, fuerzaDelSaque, anguloVertical), "Saque de centro mejorado"));
//      comandos.add(new ComandoInfo( new ComandoGolpearBalon(9, -90, 1, false), "Saque de centro"));
    }
      
    indJugadorSaca = IND_JUGADOR_10;
    if (Termineitor.puedeRematar(indJugadorSaca, sp, Termineitor.COMANDO_SAQUE))
    {
double factorRemateJugador = sp.detalleMisJugadores()[indJugadorSaca].getRemate();
double anguloVertical = PASE_ANGULO_POR_DEFECTO;
      double fuerzaDelSaque = calcularVelocidadGolpeoPelota( distanciaDelPase, factorRemateJugador, anguloVertical);
      comandos.add(new ComandoInfo( new ComandoGolpearBalon(indJugadorSaca, destinoDelSaque, fuerzaDelSaque, anguloVertical), "Saque de centro mejorado"));
//      comandos.add(new ComandoInfo( new ComandoGolpearBalon(10, -90, 1, false), "Saque de centro"));
    }
    
    // Tactica Test. Un centrocampista se aproxime al bal�n
    double DISTANCIA_RECORRIDA_BALON = 20;
    Posicion nuevoBalon = sp.balon().moverPosicion(0, DISTANCIA_RECORRIDA_BALON);
    int[] atacantes = new int[] {9, IND_JUGADOR_10};
    int jugadorCencanoAlPase = nuevoBalon.indiceMasCercano(sp.misJugadores(), atacantes);
    comandos.add(new ComandoInfo( new ComandoIrA(jugadorCencanoAlPase, nuevoBalon), "Saque de centro recepcion"));
    
    // Tactica Test. Los delanteros corren hacia adelante
    Posicion[] posicionesIniciales = getPosicionInicial(sp);
    comandos.add(new ComandoInfo( new ComandoIrA(9, posicionesIniciales[9]), "Saque de centro, delanteros sacan y dse adelantan"));
    comandos.add(new ComandoInfo( new ComandoIrA(IND_JUGADOR_10, posicionesIniciales[10]), "Saque de centro, delanteros sacan y dse adelantan"));
    
    return comandos;
  }
  protected Vector<ComandoInfo> getComandosSaqueMeta( SituacionPartido sp)
  {
    Vector<ComandoInfo> comandos = new Vector<ComandoInfo>();
    comandos.add(new ComandoInfo( new ComandoIrA(IND_JUGADOR_0, sp.balon()), "Saque de puerta"));
    
    return comandos;
  }
  protected Vector<ComandoInfo> getComandosSaqueEsquina( SituacionPartido sp)
  {
    Vector<ComandoInfo> comandos = new Vector<ComandoInfo>();
        
    // No deber�a de sacarlo un defensa
    int[] indDefensas = this.getDefensas(sp, this.getTactica(sp));
    int[] noSacaranUnCorner = new int[indDefensas.length + 2];
    for (int i = 0; i < indDefensas.length; i++)
    {
      noSacaranUnCorner[i] = indDefensas[i];
    }
    int indJugadorRemata = IND_JUGADOR_10;
    noSacaranUnCorner[noSacaranUnCorner.length - 2] = 0;  // portero
    noSacaranUnCorner[noSacaranUnCorner.length - 1] = indJugadorRemata;  // delantero centro
    int indJugadorSaca = sp.balon().indiceMasCercano(sp.misJugadores(), noSacaranUnCorner);
    
    Posicion corner = Constantes.cornerSupDer;
    Posicion destinoDelCentro = new Posicion(X_LINEA_INTERNA, Y_LINEA_DELANTEROS_CENTRO);
    if (sp.balon().getX() < 0)
    {
      corner = Constantes.cornerSupIzq;
      destinoDelCentro = new Posicion(- X_LINEA_INTERNA, Y_LINEA_DELANTEROS_CENTRO);
    }
    
    // Los Centrocampistas deben apoyar
    
    comandos.add(new ComandoInfo( new ComandoIrA(indJugadorSaca, corner), "Saque de corner"));
    
    // Tactica Test. El delanter se aproximara al bal�n
    if (Termineitor.puedeRematar(indJugadorSaca, sp, Termineitor.COMANDO_SAQUE))
    {
      double distanciaDelPase = destinoDelCentro.distancia(corner);
// Valor inicial de fuerza = 1      
//      double fuerzaDelSaque = 1.0;
double factorRemateJugador = sp.detalleMisJugadores()[indJugadorSaca].getRemate();
double anguloVertical = PASE_ANGULO_POR_DEFECTO;
      double fuerzaDelSaque = calcularVelocidadGolpeoPelota( distanciaDelPase, factorRemateJugador, anguloVertical);
//      comandos.add(new ComandoInfo( new ComandoGolpearBalon(indJugadorSaca, destinoDelCentro, 1.0, true), "Saque de corner"));
      comandos.add(new ComandoInfo( new ComandoGolpearBalon(indJugadorSaca, destinoDelCentro, fuerzaDelSaque, anguloVertical), "Saque de corner mejorado"));
    }

    comandos.add(new ComandoInfo( new ComandoIrA(indJugadorRemata, destinoDelCentro), "Saque de corner rematador"));

    return comandos;
  }
  protected Vector<ComandoInfo> getComandosSaqueBanda( SituacionPartido sp)
  {
    Vector<ComandoInfo> comandos = new Vector<ComandoInfo>();
double DISTANCIA_RECORRIDA_BALON = 20;
    
int NUMERO_JUGADORES_REMATAN = 2;
    int[] indDefensas = this.getLiberos(sp, this.getTactica(sp));
    int[] noSacaranBanda = new int[indDefensas.length + 2];
    for (int i = 0; i < indDefensas.length; i++)
    {
      noSacaranBanda[i] = indDefensas[i];
    }
    noSacaranBanda[noSacaranBanda.length - 2] = IND_JUGADOR_0;  // portero
    noSacaranBanda[noSacaranBanda.length - 1] = IND_JUGADOR_10;  // delantero centro
    int[] indJugadoresCerca = sp.balon().indicesMasCercanos(sp.misJugadores(), noSacaranBanda);
    int indJugadorSaca = indJugadoresCerca[0];
    
    double destinoY = sp.balon().getY() + DISTANCIA_RECORRIDA_BALON;
    if (destinoY > Y_LINEA_DELANTEROS_CENTRO)
      destinoY = Y_LINEA_DELANTEROS_CENTRO;
    Posicion destinoDelCentro = new Posicion(X_LINEA_INTERNA, destinoY);
    if (sp.balon().getX() < 0)
    {
      destinoDelCentro = new Posicion(- X_LINEA_INTERNA, destinoY);
    }
    
    comandos.add(new ComandoInfo( new ComandoIrA(indJugadorSaca, sp.balon()), "Saque de banda"));
    

    double distanciaDelPase = destinoDelCentro.distancia(sp.balon());

    // Tactica Test. El delanter se aproximara al bal�n
    if (Termineitor.puedeRematar(indJugadorSaca, sp, Termineitor.COMANDO_SAQUE))
    {
// Valor inicial de fuerza = 1      
//      double fuerzaDelSaque = 1.0;
double factorRemateJugador = sp.detalleMisJugadores()[indJugadorSaca].getRemate();
double anguloVertical = PASE_ANGULO_POR_DEFECTO;
      double fuerzaDelSaque = calcularVelocidadGolpeoPelota( distanciaDelPase, factorRemateJugador, anguloVertical);
      comandos.add(new ComandoInfo( new ComandoGolpearBalon(indJugadorSaca, destinoDelCentro, fuerzaDelSaque, anguloVertical), "Saque de banda mejorado"));
//      comandos.add(new ComandoInfo( new ComandoGolpearBalon(indJugadorSaca, destinoDelCentro, 1.0, true), "Saque de banda"));
    }
    int indJugadorRemata;
    for (int i = 0; i < NUMERO_JUGADORES_REMATAN; i++)
    {
      indJugadorRemata = indJugadoresCerca[i + 1];
      comandos.add(new ComandoInfo( new ComandoIrA(indJugadorRemata, destinoDelCentro), "Saque de banda rematador "+i));
    }

    return comandos;
  }
  protected Vector<ComandoInfo> crearComandoRegate( int indJugador, Posicion destinoRegate, SituacionPartido sp)
  {
    Posicion posicionJugador = sp.misJugadores()[indJugador];
    double factorRemateJugador = sp.detalleMisJugadores()[indJugador].getRemate();
    return crearComandoRegate( indJugador, destinoRegate, posicionJugador, factorRemateJugador);
  }
  protected Vector<ComandoInfo> crearComandoRegate( int indJugador, Posicion destinoRegate, 
                                                    Posicion posicionJugador, double factorRemateJugador)
  {
     Vector<ComandoInfo> comandos = new Vector<ComandoInfo>();

    // CODIGO ORIGINAL
    comandos.add( new ComandoInfo( new ComandoGolpearBalon(indJugador), "Regate original"));
    comandos.add( new ComandoInfo( new ComandoIrA(indJugador, destinoRegate), "Regate original"));
    
    return comandos;
  }
  //---------------------------------------------//
  /**
   * Retorna los comandos cuando hay una jugada ensayada.
   */
  protected Vector<ComandoInfo> crearComandoMarcaje( SituacionPartido sp, int indJugadorMio, int indJugadorRival)
  {
    Vector<ComandoInfo> comandos = new Vector<ComandoInfo>();
    
    Posicion destino = sp.rivales()[indJugadorRival];
    double angulo = destino.angulo(sp.balon());
    destino = destino.moverAngulo(angulo, MARCAJE_DISTANCIA_ANTICIPACION);
    
    comandos.add( new ComandoInfo( new ComandoIrA(indJugadorMio, destino), "Marcaje de "+indJugadorMio + "  , a rival = "+indJugadorRival));
    
    return comandos;
  }
  //---------------------------------------------//
  /**
   * Retorna las posibles jugadas ensayadas.
   */
  protected Vector<ComandoInfo> crearComandoTiroCercano(int indJugador, Posicion destinoDisparo, 
                                                        Posicion balon, Posicion[] misJugadores, Posicion[] rivales,
                                                        double factorRemateJugador)
  {
    Vector<ComandoInfo> comandos = new Vector<ComandoInfo>();
    ComandoInfo comandoTiroCercano;
    
    boolean tirar = true;
    
    double distancia = destinoDisparo.distancia(misJugadores[indJugador]);
    Posicion destinoRegate = Constantes.penalSup.moverPosicion(0, TIRO_CERCANO_METROS_DESDE_PENALTY);
    Posicion posMedia = Posicion.media( misJugadores[indJugador], destinoRegate);
    int indJugadorRivalCercano = -1;
    if (distancia > TIRO_DISTANCIA_MUY_CERCA)
    {
      int[] indRivalesCercanos = destinoRegate.indicesMasCercanos(rivales);
      Posicion posRivalCercano;
      double distanciaRival;
      tirar = false;
      for (int i = 0; i < indRivalesCercanos.length; i++)
      {
        indJugadorRivalCercano = indRivalesCercanos[i];
        posRivalCercano = rivales[indJugadorRivalCercano];
        // Rival MUY cerca ?
        distanciaRival = posRivalCercano.distancia(misJugadores[indJugador]);
        if (distanciaRival <= TIRO_CERCANO_RIVALES_CERCA_ORIGEN )
        {
          tirar = true;
          break;
        }
        // Destino de regate libre ?
        distanciaRival = posRivalCercano.distancia(destinoRegate);
        if (distanciaRival <= TIRO_CERCANO_RIVALES_CERCA_DESTINO )
        {
          tirar = true;
          break;
        }
        // Recorrido de regate libre ?
        distanciaRival = posRivalCercano.distancia(posMedia);
//        if (distanciaRival <= ((distancia/2) * TIRO_CERCANO_RIVALES_CERCA_FACTOR))
        if (distanciaRival <= (DISTANCIA_MAX_CORRE_RIVAL))
        {
          tirar = true;
          break;
        }
      }
    }
    
    
    if (tirar)
    {
double fuerza = 1;

double anguloVertical = TIRO_ANGULO_ALTO_NORMAL;
    if (balon.distancia(destinoDisparo) < TIRO_ALTO_LIMITE)
      anguloVertical = TIRO_ANGULO_ALTO_BAJO;
    
//anguloVertical = TIRO_ALTO_LIMITE;

//double TIRO_MEJOR_ALTURA = 2.5;    
//double TIRO_MEJOR_ALTURA = 2.4;    
double TIRO_MEJOR_ALTURA = 2.35;    
double alturaDestino = TIRO_MEJOR_ALTURA;

DatosPaseJugador evaluacionMejorTiro = calcularMejorTiroJugador(indJugador, balon, destinoDisparo, alturaDestino, factorRemateJugador );
//System.err.println("Termineitor.crearComandoTiroCercano() indJugador="+indJugador+"  => "+evaluacionMejorTiro+"\n");
if (evaluacionMejorTiro != null)
{
  
  fuerza = evaluacionMejorTiro.getFactorFuerza();
  anguloVertical = evaluacionMejorTiro.getAnguloVertical();
  
//System.err.println("Termineitor.crearComandoTiroCercano() indJugador="+indJugador+"  angulo="+anguloVertical+"  => "+evaluacionMejorTiro+"\n");
}

      comandoTiroCercano = new ComandoInfo( new ComandoGolpearBalon(indJugador, destinoDisparo, fuerza, anguloVertical), "Tiro mejorado cercano (rival cerca = "+indJugadorRivalCercano+" )  evaluacionMejorTiro="+evaluacionMejorTiro);
      comandos.add( comandoTiroCercano );
    }
    else
    {
//      comandoTiroCercano = new ComandoInfo( new ComandoGolpearBalon(indJugador, destinoDisparo, fuerza, anguloVertical), "Tiro mejorado cercano");
//      comandos.add( comandoTiroCercano );
      comandos.add( new ComandoInfo( new ComandoGolpearBalon(indJugador), "TiroCercano Regate "));
      comandos.add( new ComandoInfo( new ComandoIrA(indJugador, destinoRegate), "TiroCercano Regate "));
    } 

    
    
    return comandos;
  }
  protected DatosPaseJugador calcularMejorTiroJugador(int indJugador, 
                                                      Posicion balonIni,
                                                      Posicion destinoTiro,
                                                      double alturaDestino,
//                                                      double alturaDestinoMax,
                                                      double factorRemateJugador)
  {
    DatosPaseJugador evaluacionMejorTiro = null;
    
double fuerzaRemate = 1;
double anguloVer = 0;

    double alturaBalon = 0;
double diffAlturaBalon = 0;
    
    int iteraciones = 20;
    
int TIRO_MEJOR_ANGULO_INI = 20;    
int TIRO_MEJOR_ANGULO_FIN = 60;    
int TIRO_MEJOR_ANGULO_GAP = 10;
int TIRO_MEJOR_ANGULO_GAP_FINO = 2;
    
double lastDiffAltura = 100;
    for (int i = TIRO_MEJOR_ANGULO_INI; i <= TIRO_MEJOR_ANGULO_FIN; i = i + TIRO_MEJOR_ANGULO_GAP)
    {
      anguloVer = i;
      alturaBalon = calcularAlturaTiroJugador(balonIni, destinoTiro, anguloVer, fuerzaRemate, factorRemateJugador, iteraciones);
      diffAlturaBalon = Math.abs( alturaBalon - alturaDestino);
      if (diffAlturaBalon < CALCULAR_TIRO_DISTANCIA_MINIMA_APROXIMACION_Z)
      {
        evaluacionMejorTiro = new DatosPaseJugador(indJugador, -1, balonIni, destinoTiro, fuerzaRemate, anguloVer, 0);
        break;
      }
      else if (diffAlturaBalon > lastDiffAltura)
      {
        // Angulo excedido. Localizar uno mejor entre i e i-1
        lastDiffAltura = diffAlturaBalon;
        for (int j = (i-TIRO_MEJOR_ANGULO_GAP); j <= i; j = j + TIRO_MEJOR_ANGULO_GAP_FINO)
        {
          alturaBalon = calcularAlturaTiroJugador(balonIni, destinoTiro, j, fuerzaRemate, factorRemateJugador, iteraciones);
          diffAlturaBalon = Math.abs( alturaBalon - alturaDestino);
          if (diffAlturaBalon < CALCULAR_TIRO_DISTANCIA_MINIMA_APROXIMACION_Z)
          {
            evaluacionMejorTiro = new DatosPaseJugador(indJugador, -1, balonIni, destinoTiro, fuerzaRemate, j, 0);
            break;
          }
          else if (diffAlturaBalon > lastDiffAltura)
          {
            // :-(
            break;
          }
          lastDiffAltura = diffAlturaBalon;
        }
        break;
      }
      lastDiffAltura = diffAlturaBalon;
    }
    
    return evaluacionMejorTiro;
  }
  protected double calcularAlturaTiroJugador( Posicion balonIni,
                                              Posicion destinoTiro,
                                              double anguloVer,
                                              double fuerzaRemate,
                                              double factorRemateJugador,
                                              int iteraciones)
  {
    double alturaDestino = -1;
    
    double angulo = balonIni.angulo(destinoTiro);
    
    double vel = fuerzaRemate * Constantes.getVelocidadRemate( factorRemateJugador );
    double angVer = Math.min(anguloVer, Constantes.ANGULO_VERTICAL_MAX);

    angVer = Math.max(angVer, 0);
    angVer = angVer * Math.PI / 180d;
    vel = vel * Math.cos(angVer);
    double balonDz = redondeaMultiplo(vel * Math.sin(angVer), Constantes.G);
    //calcula la velocidad en el plano x/y
    double balonDx = Math.cos(angulo) * vel;
    double balonDy = Math.sin(angulo) * vel;
    //establece la altura inicial en cero
    double alturaBalon = 0;
    
    boolean suelo = false;
    Posicion balon = new Posicion(balonIni);
    double lastDistancia = 100;
    double currentDistancia;
    for (int i=0; i<iteraciones; i++)
    {
      if (!suelo && alturaBalon == 0 && balonDz >= 0 && balonDz < Constantes.G * 3) {//condicion para que se arrastre
          suelo = true;
      }
      if (suelo) {
          balon = balon.moverPosicion(balonDx, balonDy);
          balonDx = balonDx * Constantes.FACTOR_DISMINUCION_VEL_BALON_SUELO;
          balonDy = balonDy * Constantes.FACTOR_DISMINUCION_VEL_BALON_SUELO;
      } else {
          alturaBalon = redondeaMultiplo(alturaBalon + balonDz, Constantes.G);
          balon = balon.moverPosicion(balonDx, balonDy);
          balonDz = redondeaMultiplo(balonDz - Constantes.G, Constantes.G);
          balonDx = balonDx * Constantes.FACTOR_DISMINUCION_VEL_BALON_AIRE;
          balonDy = balonDy * Constantes.FACTOR_DISMINUCION_VEL_BALON_AIRE;
          if (alturaBalon == 0) {
              balonDz = (-balonDz - Constantes.G) * Constantes.FACTOR_DISMINUCION_ALTURA_BALON_REBOTE;
              balonDz = redondeaMultiplo(balonDz, Constantes.G);
          }
      }
      currentDistancia = balon.distancia(destinoTiro);
      if (currentDistancia > lastDistancia)
      {
        alturaDestino = alturaBalon;
        break;
      }
      lastDistancia = currentDistancia;
    }
    return alturaDestino;
  }
  protected JugadaEnsayada[] crearJugadasEnsayadas( )
  {
    JugadaEnsayada[] misJugadaEnsayadas;
    
    if (JUGADA_ENSAYADA_USAR)
    {
//    misJugadaEnsayadas = new JugadaEnsayada[4];
misJugadaEnsayadas = new JugadaEnsayada[5];
    
      // Jugada MedioCentro
      misJugadaEnsayadas[0] =  new JugadaEnsayadaAvanzayPasa( 5, 
                               new Posicion(X_LINEA_CENTRAL, Y_LINEA_MEDIOCAMPO),
                               new int[] {8, 6, 7},
                               new Posicion[] { 
                                               new Posicion( X_LINEA_MEDIO, Y_LINEA_DELANTEROS),
                                               new Posicion( - X_LINEA_ABIERTA, Y_LINEA_MEDIAPUNTA),
                                               new Posicion( X_LINEA_MEDIO, Y_LINEA_MEDIAPUNTA)
                                               },
                               TIRO_DISTANCIA_NUNCA,
                               "Jugada MedioCentro"
                               );
  
      // Jugada MediaPunta
      misJugadaEnsayadas[1] =  new JugadaEnsayadaAvanzayPasa( 8, 
                               new Posicion(- X_LINEA_INTERNA, Y_LINEA_DELANTEROS),
                               new int[] {IND_JUGADOR_10, 9},
                               new Posicion[] { 
                                               new Posicion( X_LINEA_CENTRAL, Constantes.penalSup.getY()),  //Y_LINEA_DELANTEROS_CENTRO),
                                               new Posicion( X_LINEA_INTERNA, Y_LINEA_DELANTEROS_CENTRO)
                                               },
                               TIRO_DISTANCIA_MEDIO,
                               "Jugada MediaPunta"
                               );
      
      // Jugada Banda Derecha
      misJugadaEnsayadas[2] =  new JugadaEnsayadaAvanzayPasa( 7, 
                               new Posicion( X_LINEA_MEDIO, Y_LINEA_MEDIAPUNTA),
                               new int[] {IND_JUGADOR_10, 9, 8},
                               new Posicion[] { 
                                               new Posicion( X_LINEA_CENTRAL, Y_LINEA_DELANTEROS_CENTRO),
                                               new Posicion( X_LINEA_MEDIO, Y_LINEA_DELANTEROS_CENTRO),
                                               new Posicion( - X_LINEA_INTERNA, Y_LINEA_DELANTEROS)
                                               },
                               TIRO_DISTANCIA_MEDIO,
                               "Jugada Banda Derecha"
                               );
      
      // Jugada Banda Izquierda
      misJugadaEnsayadas[3] =  new JugadaEnsayadaAvanzayPasa( 6, 
                               new Posicion( - X_LINEA_ABIERTA, Y_LINEA_MEDIAPUNTA),
                               new int[] {IND_JUGADOR_10, 8, 9},
                               new Posicion[] { 
                                               new Posicion( - X_LINEA_INTERNA, Y_LINEA_DELANTEROS_CENTRO),
                                               new Posicion( - X_LINEA_INTERNA, Y_LINEA_DELANTEROS),
                                               new Posicion( X_LINEA_INTERNA, Y_LINEA_DELANTEROS)
                                               },
                               TIRO_DISTANCIA_MEDIO,
                               "Jugada Banda Izquierda"
                               );
      // Jugada Delantero
      misJugadaEnsayadas[4] =  new JugadaEnsayadaAvanzayPasa( 9, 
                               new Posicion( X_LINEA_INTERNA, Constantes.penalSup.getY()),
                               new int[] {IND_JUGADOR_10},
                               new Posicion[] { 
                                               new Posicion( - X_LINEA_INTERNA, Y_LINEA_DELANTEROS_CENTRO)
                                               },
                               TIRO_DISTANCIA_CERCA,
                               "Jugada Delantero"
                               );
    }
    else
    {
      misJugadaEnsayadas = new JugadaEnsayada[0];
    }
    
    return misJugadaEnsayadas;
  }
  protected Vector<ComandoInfo> getComandosJugada( SituacionPartido sp)
  {
    Vector<ComandoInfo> comandos = new Vector<ComandoInfo>();
    
    if (JUGADA_ENSAYADA_USAR)
    {
      if (this.jugadaEnsayadaActual == JUGADA_ENSAYADA_NINGUNA)
      {
        for (int i = 0; i < this.jugadasEnsayadas.length; i++)
        {
          if (this.jugadasEnsayadas[i].checkIniciarJugada(sp))
          {
            this.jugadaEnsayadaActual = this.jugadasEnsayadas[i];
            
MENSAJE = "Jugada = " + this.jugadaEnsayadaActual.getNombreJugada();

            break;
          }
        }
        
      }
      if (this.jugadaEnsayadaActual != JUGADA_ENSAYADA_NINGUNA)
      {
        if (this.jugadaEnsayadaActual.checkPararJugada(sp))
        {
          this.jugadaEnsayadaActual = JUGADA_ENSAYADA_NINGUNA;
        }
        else
        {

          comandos = this.jugadaEnsayadaActual.getComandosJugada(sp );
        }
      }
      else
MENSAJE = "";        
    }
        
    return comandos;
  }
  
  //---------------------------------------------//
  /**
   * 
   */
  public List<Comando> ejecutar(SituacionPartido sp)
  {
    LinkedList<Comando> comandos = new LinkedList<Comando>();
    
    try
    {
      // Movimiento Inicial
      Vector<ComandoInfo> comandosMovimientoInicial = movimientoInicial(sp);
      
      // Movimiento por l�neas
      Vector<ComandoInfo> comandosMovimientoPortero = movimientoPortero(sp);
      Vector<ComandoInfo> comandosMovimientoDefensa = movimientoDefensa(sp);
      Vector<ComandoInfo> comandosMovimientoMedia = movimientoMedia(sp);
      Vector<ComandoInfo> comandosMovimientoAtacantes = movimientoAtacantes(sp);
      // Achique
      Vector<ComandoInfo> comandosMovimientoAchique = movimientoAchique(sp);
      
      // Pases por Defecto
      Vector<ComandoInfo> comandosGolpeoBalonPaseDefecto = golpeoBalonPaseDefecto(sp);
      
      // Pases Mejorados
      Vector<ComandoInfo> comandosGolpeoBalonPaseMejorado = golpeoBalonPaseMejorado(sp);
      
      // Regates
      Vector<ComandoInfo> comandosGolpeoBalonRegate = golpeoBalonRegate(sp);
      
      // Disparo
      Vector<ComandoInfo> golpeoBalonTiro = golpeoBalonTiro(sp);
      
      // Comprobar el Saque a bal�n parado
      Vector<ComandoInfo> comandosSaque = getComandosSaque(sp);
  
      // Realizar Jugadas Predefinidas : Implica el uso de un estado definido en la t�ctica
      Vector<ComandoInfo> comandosJugada = getComandosJugada(sp);
  
      
      // Incluir los comandos
      // --------------------
      Vector<ComandoInfo> comandosInfoVector = new Vector<ComandoInfo>();
  
      comandosInfoVector.addAll(comandosMovimientoInicial);
      
      comandosInfoVector.addAll(comandosMovimientoPortero);
      comandosInfoVector.addAll(comandosMovimientoDefensa);
      comandosInfoVector.addAll(comandosMovimientoMedia);
      comandosInfoVector.addAll(comandosMovimientoAtacantes);
      
      comandosInfoVector.addAll(comandosMovimientoAchique);
      
      comandosInfoVector.addAll(comandosGolpeoBalonPaseDefecto);
      comandosInfoVector.addAll(comandosGolpeoBalonPaseMejorado);
      
      comandosInfoVector.addAll(comandosGolpeoBalonRegate);
      
      comandosInfoVector.addAll(golpeoBalonTiro);
      
      comandosInfoVector.addAll(comandosSaque);

// JUGADAS ENSAYADAS !!!      
      comandosInfoVector.addAll(comandosJugada);
      
      
      // Analizar los datos de la situacion actual
      this.analizaDatosPosicionActual(sp);
      // Actualizar los datos de la situacion actual
      this.almacenaDatosPosicionActual(sp, comandosInfoVector);
      
//      printComandos(sp, comandosInfoVector);
      
      boolean eliminarPrimerosGolpeosBalonMismoJugador = true;
      comandos = addComandosToList(comandosInfoVector, comandos, eliminarPrimerosGolpeosBalonMismoJugador);
      
//System.out.println( toStringRecursive(comandos));
      
    }
    catch (Throwable e)
    {
      e.printStackTrace();

    }
    
    return comandos;
  }

  protected String getTimeFromIteracion( SituacionPartido sp)
  {
    String time = "0";
    int sec = sp.iteracion() * Constantes.DELAY / 1000;
    time = time + sec / 60 + ":";
    sec = sec % 60;
    if (sec < 10) {
        time = time + "0";
    }
    time = time + sec;
    return time;
  }
  
  //---------------------------------------------//
  /**
   * 
   */
  protected void analizaDatosPosicionActual( SituacionPartido sp )
  {
//    if (SHOW_INFO_BALON)
    {
      double currentVelocidadBalon = sp.balon().distancia(this.lastBalon);
      double MARGEN_ANGULAR = 0.001;
      double currentAnguloBalon = sp.balon().angulo(this.lastBalon);
      // �� CUIDADO con los valores negativos !!
      if ((currentVelocidadBalon > this.lastVelocidadBalon) || 
          (Math.abs(currentAnguloBalon - this.lastAnguloBalon) > MARGEN_ANGULAR))
      {
        this.lastEstimadoIteracionChute = sp.iteracion();
        this.lastEstimadoChutadorPropio = NADIE;
        this.lastEstimadoChutadorRival = NADIE;

        int[] jugadoresPropiosCercaBalon = sp.balon().indicesMasCercanos(sp.misJugadores());
        for (int i = 0; i < jugadoresPropiosCercaBalon.length; i++)
        {
          if ( sp.iteracionesParaRematar()[jugadoresPropiosCercaBalon[i]] == Constantes.ITERACIONES_GOLPEAR_BALON)
//          if (Termineitor.puedeRematar(jugadoresPropiosCercaBalon[i], sp, COMANDO_DEBUG))
          {
            this.lastEstimadoChutadorPropio = jugadoresPropiosCercaBalon[i];
          }
        }
        int[] jugadoresRivalesCercaBalon = sp.balon().indicesMasCercanos(sp.rivales());
        for (int i = 0; i < jugadoresRivalesCercaBalon.length; i++)
        {
          if ( sp.iteracionesParaRematarRival()[jugadoresRivalesCercaBalon[i]] == Constantes.ITERACIONES_GOLPEAR_BALON)
//          if (Termineitor.puedeRematar(jugadoresRivalesCercaBalon[i], sp, COMANDO_DEBUG))
          {
            this.lastEstimadoChutadorRival = jugadoresRivalesCercaBalon[i];
          }
        }
        if ((this.lastEstimadoChutadorPropio > NADIE) &&
            (this.lastEstimadoChutadorRival > NADIE))
        {
          ComandoInfo comandoInfo = null;
          for (int i=0; i<this.lastComandos.size(); i++)
          {
            comandoInfo = (ComandoInfo) this.lastComandos.elementAt(this.lastComandos.size() - i);
            if ((comandoInfo.getComando().getIndJugador() == this.lastEstimadoChutadorPropio) &&
                (comandoInfo.getComando() instanceof ComandoGolpearBalon))
            {
              double angulo = ((ComandoGolpearBalon) comandoInfo.getComando()).getAnguloRemate();
              if (((ComandoGolpearBalon) comandoInfo.getComando()).isCoordenada())
              {
                Posicion destino = ((ComandoGolpearBalon) comandoInfo.getComando()).getDestinoBalon();
                angulo = sp.balon().angulo(destino);
              }
              if (Math.abs(currentAnguloBalon - angulo) > MARGEN_ANGULAR)
              {
                this.lastEstimadoChutadorRival = NADIE;
              }
            }
          }
        }
        String title = "Balon Golpeado";
        String infoDestino = "";
        Posicion destino = null;
        if (SHOW_INFO_BALON_CALCULAR_DESTINO)
        {
          destino = calcularDestinoPelota(sp);
          this.lastBalonDestinoCalculado = destino;
          infoDestino = "\t Pelota hacia = " + destino;
          
          if (destino.getY() > Constantes.centroArcoSup.getY())
            title = title + " Hacia porter�a rival  *** ";
          else if (destino.getY() < Constantes.centroArcoInf.getY())
            title = title + " Hacia porter�a local  *** ";
        }
        
        if (this.lastEstimadoChutadorPropio != NADIE)
        {
          if ((destino != null) && (((destino.getY() + 5) > Constantes.centroArcoSup.getY())))
            this.tirosTotalesMios++;
        }
        else if (this.lastEstimadoChutadorRival != NADIE)
        {
          if ((destino != null) && (((destino.getY() - 5) > Constantes.centroArcoInf.getY())))
            this.tirosTotalesRival++;
        }
      }
    }
    
    analizaDatosPosicionActualParaCambiarTactica(sp);
    
  }
  protected void analizaDatosPosicionActualParaCambiarTactica( SituacionPartido sp )
  {
    if ((sp.iteracion() % ANALISIS_ITERACIONES ) == 0)
    {
      int golesGanando = sp.golesPropios() - sp.golesRival();
       
      if (this.analisisMarcadorMio.size() == 0)
      {
        this.analisisActualJugadas = ANALISIS_MANTENER_ORIGINAL;
        this.analisisActualTiros = ANALISIS_MANTENER_ORIGINAL;
        this.analisisActualDefensa = ANALISIS_MANTENER_ORIGINAL;
      }
      else if (this.analisisMarcadorMio.size() > 1)
      {
        int lastGolesMios = ((Integer) this.analisisMarcadorMio.elementAt(this.analisisMarcadorMio.size() - 1)).intValue();
        int lastGolesRival = ((Integer) this.analisisMarcadorRival.elementAt(this.analisisMarcadorRival.size() - 1)).intValue();
        int lastGolesGanando = lastGolesMios - lastGolesRival;
//        analizaCambiarTacticaJugadas();
//        analizaCambiarTacticaTiros();
//        analizaCambiarTacticaDefensa();

StringBuffer texto = new StringBuffer("");
texto.append(" * analisisActualJugadas : ");
        // Jugadas
        if (this.analisisActualJugadas == ANALISIS_MANTENER_ORIGINAL)
        {
texto.append("ANALISIS_MANTENER_ORIGINAL : "+golesGanando + " last = " +lastGolesGanando);
          // ANALISIS_MANTENER_ORIGINAL , ANALISIS_ESTUDIAR_ALTERNATIVA
          if (golesGanando <= 0)
          {
texto.append(" a  ANALISIS_ESTUDIAR_ALTERNATIVA : ");
            this.analisisActualJugadas = ANALISIS_ESTUDIAR_ALTERNATIVA;
          }
        }
        else if (this.analisisActualJugadas == ANALISIS_ESTUDIAR_ALTERNATIVA)
        {
texto.append("ANALISIS_ESTUDIAR_ALTERNATIVA : "+golesGanando + " last = " +lastGolesGanando);
          // ANALISIS_MANTENER_ORIGINAL , ANALISIS_ESTUDIAR_ALTERNATIVA , ANALISIS_MANTENER_ALTERNATIVA
          if (golesGanando <= 0)
          {
             if (golesGanando < lastGolesGanando)
            {
              this.analisisActualJugadas = ANALISIS_MANTENER_ALTERNATIVA;
texto.append(" a  ANALISIS_MANTENER_ALTERNATIVA : ");
            }
          }
          else
          {
            this.analisisActualJugadas = ANALISIS_MANTENER_ORIGINAL;
texto.append(" a  ANALISIS_MANTENER_ORIGINAL : ");
          }
        }
        else if (this.analisisActualJugadas == ANALISIS_MANTENER_ALTERNATIVA)
        {
texto.append("ANALISIS_MANTENER_ORIGINAL : "+golesGanando + " last = " +lastGolesGanando);
          // ANALISIS_MANTENER_ORIGINAL , ANALISIS_MANTENER_ALTERNATIVA
           if (golesGanando < lastGolesGanando)
          {
texto.append(" a  ANALISIS_MANTENER_ORIGINAL : ");
            this.analisisActualJugadas = ANALISIS_MANTENER_ORIGINAL;
          }
        }
        if (this.analisisActualJugadas == ANALISIS_MANTENER_ORIGINAL)
        {
          JUGADA_ENSAYADA_USAR = true;
        }
        else if (this.analisisActualJugadas == ANALISIS_ESTUDIAR_ALTERNATIVA)
        {
          JUGADA_ENSAYADA_USAR = true;
        }
        else if (this.analisisActualJugadas == ANALISIS_MANTENER_ALTERNATIVA)
        {
          JUGADA_ENSAYADA_USAR = false;
        }

        // Tiros
        boolean tendenciaNegativa = true;
        if (analisisMarcadorTirosMios.size() <= 2)
        {
          tendenciaNegativa = false;
        }
        else
        {
          double punteria;
          double lastPunteria = -1;
          int golesIntevalo, tirosIntervalo;
          for (int i = 1; i < analisisMarcadorTirosMios.size(); i++)
          {
            golesIntevalo = ((Integer) this.analisisMarcadorMio.elementAt(i)).intValue() -
                            ((Integer) this.analisisMarcadorMio.elementAt(i - 1)).intValue();          
            tirosIntervalo = ((Integer) this.analisisMarcadorTirosMios.elementAt(i)).intValue() -
                             ((Integer) this.analisisMarcadorTirosMios.elementAt(i - 1)).intValue();   
            if (tirosIntervalo > 0)
              punteria = ((double) golesIntevalo) / ((double) tirosIntervalo);
            else
              punteria = 0;
            
            if (i == 1)
              lastPunteria = punteria;
            else if (punteria > lastPunteria)
            {
              tendenciaNegativa = false;
              break;
            }
            lastPunteria = punteria;
          }
        }
texto.append("\t * analisisActualTiros : ");
        if (this.analisisActualTiros == ANALISIS_MANTENER_ORIGINAL)
        {
texto.append("ANALISIS_MANTENER_ORIGINAL :  tendenciaNegativa = " +tendenciaNegativa);
          // ANALISIS_MANTENER_ORIGINAL , ANALISIS_ESTUDIAR_ALTERNATIVA
          if (tendenciaNegativa)
          {
texto.append(" a  ANALISIS_ESTUDIAR_ALTERNATIVA : ");
            this.analisisActualTiros = ANALISIS_ESTUDIAR_ALTERNATIVA;
          }
        }
        else if (this.analisisActualTiros == ANALISIS_ESTUDIAR_ALTERNATIVA)
        {
texto.append("ANALISIS_ESTUDIAR_ALTERNATIVA :  tendenciaNegativa = " +tendenciaNegativa);
          // ANALISIS_MANTENER_ORIGINAL , ANALISIS_ESTUDIAR_ALTERNATIVA , ANALISIS_MANTENER_ALTERNATIVA
          if (tendenciaNegativa)
          {
texto.append(" a  ANALISIS_MANTENER_ALTERNATIVA : ");
            this.analisisActualTiros = ANALISIS_MANTENER_ALTERNATIVA;
          }
          else
          {
texto.append(" a  ANALISIS_MANTENER_ORIGINAL : ");
            this.analisisActualTiros = ANALISIS_MANTENER_ORIGINAL;
          }
        }
        else if (this.analisisActualTiros == ANALISIS_MANTENER_ALTERNATIVA)
        {
texto.append("ANALISIS_MANTENER_ALTERNATIVA :  tendenciaNegativa = " +tendenciaNegativa);
          // ANALISIS_MANTENER_ORIGINAL , ANALISIS_MANTENER_ALTERNATIVA
          if (tendenciaNegativa)
          {
texto.append(" a  ANALISIS_MANTENER_ORIGINAL : ");
            this.analisisActualTiros = ANALISIS_MANTENER_ORIGINAL;  // No hemos arreglado nada :-(
          }
        }
        if (this.analisisActualTiros == ANALISIS_MANTENER_ORIGINAL)
        {
          TIRO_CERCANO_METROS_DESDE_PENALTY = TIRO_CERCANO_METROS_DESDE_PENALTY_5;
        }
        else if (this.analisisActualTiros == ANALISIS_ESTUDIAR_ALTERNATIVA)
        {
          TIRO_CERCANO_METROS_DESDE_PENALTY = TIRO_CERCANO_METROS_DESDE_PENALTY_5;
        }
        else if (this.analisisActualTiros == ANALISIS_MANTENER_ALTERNATIVA)
        {
          TIRO_CERCANO_METROS_DESDE_PENALTY = TIRO_CERCANO_METROS_DESDE_PENALTY_3;
        }

        // Defensa
        tendenciaNegativa = true;
        if (analisisMarcadorRival.size() <= 2)
        {
          tendenciaNegativa = false;
        }
        else
        {
          int golesIntevalo = 0;
          int lastGolesIntevalo = 0;
          for (int i = 1; i < analisisMarcadorRival.size(); i++)
          {
            golesIntevalo = ((Integer) this.analisisMarcadorRival.elementAt(i)).intValue();   
            
            if (golesIntevalo == lastGolesIntevalo)
            {
              tendenciaNegativa = false;
              break;
            }
            lastGolesIntevalo = golesIntevalo;
          }
        }
texto.append("\t * analisisActualDefensa : ");
        if (this.analisisActualDefensa == ANALISIS_MANTENER_ORIGINAL)
        {
texto.append("ANALISIS_MANTENER_ORIGINAL :  tendenciaNegativa = " +tendenciaNegativa);
          // ANALISIS_MANTENER_ORIGINAL , ANALISIS_ESTUDIAR_ALTERNATIVA
          if (tendenciaNegativa)
          {
texto.append(" a  ANALISIS_ESTUDIAR_ALTERNATIVA : ");
            this.analisisActualDefensa = ANALISIS_ESTUDIAR_ALTERNATIVA;
          }
        }
        else if (this.analisisActualDefensa == ANALISIS_ESTUDIAR_ALTERNATIVA)
        {
texto.append("ANALISIS_ESTUDIAR_ALTERNATIVA :  tendenciaNegativa = " +tendenciaNegativa);
          // ANALISIS_MANTENER_ORIGINAL , ANALISIS_ESTUDIAR_ALTERNATIVA , ANALISIS_MANTENER_ALTERNATIVA
          if (tendenciaNegativa)
          {
texto.append(" a  ANALISIS_MANTENER_ALTERNATIVA : ");
            this.analisisActualDefensa = ANALISIS_MANTENER_ALTERNATIVA;
          }
          else
          {
texto.append(" a  ANALISIS_MANTENER_ORIGINAL : ");
            this.analisisActualDefensa = ANALISIS_MANTENER_ORIGINAL;
          }
        }
        else if (this.analisisActualDefensa == ANALISIS_MANTENER_ALTERNATIVA)
        {
texto.append("ANALISIS_MANTENER_ALTERNATIVA :  tendenciaNegativa = " +tendenciaNegativa);
          // ANALISIS_MANTENER_ORIGINAL , ANALISIS_MANTENER_ALTERNATIVA
          if (tendenciaNegativa)
          {
texto.append(" a  ANALISIS_MANTENER_ORIGINAL : ");
            this.analisisActualDefensa = ANALISIS_MANTENER_ORIGINAL;  // No hemos arreglado nada :-(
          }
        }
        if (this.analisisActualDefensa == ANALISIS_MANTENER_ORIGINAL)
        {
          MARCAJE_TODOS_DEFENSAS = false;
        }
        else if (this.analisisActualDefensa == ANALISIS_ESTUDIAR_ALTERNATIVA)
        {
          MARCAJE_TODOS_DEFENSAS = false;
        }
        else if (this.analisisActualDefensa == ANALISIS_MANTENER_ALTERNATIVA)
        {
          MARCAJE_TODOS_DEFENSAS = true;
        }

//System.err.println("Termineitor.analizaDatosPosicionActualParaCambiarTactica() Jugadas="+texto.toString());            
      }
      
      // Por �ltimo se actualzian los datos actuales
      this.analisisMarcadorMio.add( new Integer(sp.golesPropios()));
      this.analisisMarcadorRival.add( new Integer(sp.golesRival()));
      this.analisisMarcadorTirosMios.add( new Integer(this.tirosTotalesMios));
      this.analisisMarcadorTirosRival.add( new Integer(this.tirosTotalesRival));
      
      
    }   
  }
  protected void almacenaDatosPosicionActual( SituacionPartido sp, Vector<ComandoInfo> comandos)
  {
    this.lastAnguloBalon = sp.balon().angulo(this.lastBalon);
//    this.lastLastVelocidadBalon = this.lastVelocidadBalon;
    this.lastVelocidadBalon = sp.balon().distancia(this.lastBalon);
    this.lastAlturaBalon = sp.alturaBalon();
    this.lastBalon = sp.balon();
    
    this.lastGolesPropios = sp.golesPropios();
    this.lastGolesRival = sp.golesRival();
    
    this.lastComandos = comandos;
    for (int i = 0; i < this.lastMisJugadores.length; i++)
    {
      this.lastMisJugadores[i] = new Posicion(sp.misJugadores()[i]);
      this.lastRivalJugadores[i] = new Posicion(sp.rivales()[i]);
    }
    
  }
  
  public class ComandoInfo
  {
    private Comando comando;  
    private String descripcion;
//      private int prioridad;  // �?
      
    public ComandoInfo( Comando comando, String descripcion)
    {
      this.comando = comando;  
      this.descripcion = descripcion;  
    }
    
    public Comando getComando()
    {
      return this.comando;
    }
    
    public String toString()
    {
      StringBuffer result = new StringBuffer();
      if (this.comando instanceof ComandoIrA)
      {
        int indJugador = ((ComandoIrA) this.comando).getIndJugador();
        Posicion posicion = ((ComandoIrA) this.comando).getIrA();
        result.append("ComandoIrA : Jugador = " + indJugador + " ("+(indJugador+1)+")" + "\t Posicion = " + posicion);
      }
      else
      {
        int indJugador = ((ComandoGolpearBalon) this.comando).getIndJugador();
        Posicion posicion = ((ComandoGolpearBalon) this.comando).getDestinoBalon();
        double fuerzaRemate = ((ComandoGolpearBalon) this.comando).getFuerzaRemate();
        double anguloRemate = ((ComandoGolpearBalon) this.comando).getAnguloRemate();
        double anguloVertical = ((ComandoGolpearBalon) this.comando).getAnguloVertical();
        result.append("ComandoGolpearBalon : Jugador = " + indJugador + " ("+(indJugador+1)+")" + "\t Posicion = " + posicion + 
                        "\t FuerzaRemate = " + fuerzaRemate + "\t AnguloRemate = " + anguloRemate + "\t AnguloVertical = " + anguloVertical);
      }
      result.append("\t Descripcion = " + this.descripcion);
      return result.toString();
    }
    
  }
  public class DatosPase implements Comparable
  {
    private Posicion origen;  
    private Posicion destino;
    private double factorFuerza;
    private double anguloVertical;
    private int evaluacionPase;
      
    public DatosPase( Posicion origen, Posicion destino, double factorFuerza, double anguloVertical, int evaluacionPase)
    {
      this.origen = origen;  
      this.destino = destino;  
      this.factorFuerza = factorFuerza;  
      this.anguloVertical = anguloVertical;  
      this.evaluacionPase = evaluacionPase;  
    }
    
    protected double getAnguloVertical()
    {
      return anguloVertical;
    }

    protected Posicion getDestino()
    {
      return destino;
    }

    protected double getFactorFuerza()
    {
      return factorFuerza;
    }

    protected Posicion getOrigen()
    {
      return origen;
    }

    protected int getEvaluacionPase()
    {
      return evaluacionPase;
    }

    public int compareTo(Object arg0)
    {
      int resultado = 0;
      if (arg0 == null)
      {
        resultado = 1;
      }
      else if (arg0 instanceof DatosPase)
      {
        resultado = (this.evaluacionPase - ((DatosPase) arg0).getEvaluacionPase());
      }
      return resultado;
    }
    
    public String toString()
    {
      String str = "DatosPase = ";
      str = str + "origen = "+origen;
      str = str + "\t destino = "+destino;
      str = str + "\t evaluacionPase = "+evaluacionPase;
      str = str + "\t factorFuerza = "+factorFuerza;
      str = str + "\t anguloVertical = "+anguloVertical;
      return str;
    }
  }
//public class JugadaEnsayadaAvanzayPasa extends TacticaTermineitor implements JugadaEnsayada 
public class JugadaEnsayadaAvanzayPasa extends Termineitor implements JugadaEnsayada 
{
  
  protected int indJugadorInicial;
  protected int[] indJugadoresDesmarque;

  protected Posicion destinoRegateJugadorInicial;
  protected Posicion[] destinoRegateJugadoresDesmarque;
  
  protected double distanciaTiroPuerta;
  
  private String nombreJugada;
  
  private DatosPaseJugador datosPase = null;
  private double lastDistanciaBalonPase = -1;
  
  private Posicion lastDestinoRegate = null;
  private Posicion lastBalon = null;
  
  private static final int LIMITE_TURNOS = 10 * 20;
  private int turnosJugadaActual = 0;
  
  
  protected static final int FASE_NINGUNA = -1; 
  protected static final int FASE_INICIAL = 0; 
  protected static final int FASE_REGATE = 1; 
  protected static final int FASE_PASE = 2; 
  protected static final int FASE_RECEPCION = 3; 
  protected static final int FASE_TIRO = 4; 
  protected static final int FASE_FINAL = 5; 
  protected int faseActual = FASE_NINGUNA; 
  
  public JugadaEnsayadaAvanzayPasa( int indJugadorInicial, Posicion destinoRegateJugadorInicial,
                                    int[] indJugadoresDesmarque, Posicion[] destinoRegateJugadoresDesmarque,
                                    double distanciaTiroPuerta,
                                    String nombreJugada)
  {
    this.indJugadorInicial = indJugadorInicial;
    this.destinoRegateJugadorInicial = destinoRegateJugadorInicial;
    this.indJugadoresDesmarque = indJugadoresDesmarque;
    this.destinoRegateJugadoresDesmarque = destinoRegateJugadoresDesmarque;
    this.distanciaTiroPuerta = distanciaTiroPuerta;
    this.nombreJugada = nombreJugada;
  }
  public boolean checkIniciarJugada( SituacionPartido sp)
  {
    boolean result = false;
    // Si el jugador indicado puede rematar, podr� iniciarse la jugada.
    result = Termineitor.puedeRematar( indJugadorInicial, sp, Termineitor.COMANDO_JUGADA);
    
    if (result)
    {
      this.faseActual = FASE_INICIAL;
      this.turnosJugadaActual = 0;
    }
      
    return result;
  }
  public boolean checkPararJugada( SituacionPartido sp)
  {
    boolean result = false;
    
    if (this.turnosJugadaActual > LIMITE_TURNOS)
    {
      result = true;
    }
    else
    {
      if ((this.faseActual == FASE_FINAL) || (this.faseActual == FASE_NINGUNA)) 
        result = true;
      
      
  if ((this.faseActual == FASE_INICIAL) || (this.faseActual == FASE_REGATE) || (this.faseActual == FASE_PASE) )
  {
      
      Posicion posicionJugador = sp.misJugadores()[indJugadorInicial];
      double distanciaBalon = sp.balon().distancia(posicionJugador);
      if (distanciaBalon > Termineitor.REGATE_DISTANCIA_BALON_LEJOS)
      {
        result = true;
      }
  
  }
  
  if ((this.faseActual == FASE_RECEPCION))
  {
    if ((this.lastDistanciaBalonPase > 0) && (this.datosPase != null))
    {
      double newDistanciaBalonPase = this.datosPase.getDestino().distancia(sp.balon());
      if (newDistanciaBalonPase >= this.lastDistanciaBalonPase)
      {
        result = true;
      }
    }
  }
    }
    return result;
  }
  public Vector<ComandoInfo> getComandosJugada( SituacionPartido sp )
  {
    Vector<ComandoInfo> comandos = new Vector<ComandoInfo>();
    
this.turnosJugadaActual ++;
    
    
    if ((this.faseActual != FASE_NINGUNA) && (this.faseActual != FASE_FINAL))
    {
      if ((this.faseActual == FASE_INICIAL) || (this.faseActual == FASE_REGATE))
      {
        Posicion posJugador = sp.misJugadores()[indJugadorInicial];
        if (posJugador.distancia(Constantes.centroArcoSup) <= this.distanciaTiroPuerta)
        {
          // Se puede disparar a puerta
          this.faseActual = FASE_TIRO;
        }
        if (posJugador.getY() >= this.destinoRegateJugadorInicial.getY())
        {
          this.faseActual = FASE_PASE;
        }
        else
        {
          // Regatear si no hay rivales cerca
          int indRivalMasCercano;
          Posicion posicionRivalMasCercano;
          indRivalMasCercano = posJugador.indiceMasCercano(sp.rivales());
          posicionRivalMasCercano = sp.rivales()[indRivalMasCercano];
//          if (posJugador.distancia(posicionRivalMasCercano) >= REGATE_LIMITE_RIVALES)
if (Termineitor.puedeRematar(indJugadorInicial, sp, Termineitor.COMANDO_JUGADA))
{
          Posicion destinoMejorRegate = calcularMejorRegate(indJugadorInicial, sp, this.destinoRegateJugadorInicial, REGATE_LIMITE_ITERACIONES_POR_REGATE);
  
          // if (puedeRegatear( indJugadorInicial, sp, COMANDO_JUGADA, this.destinoRegateJugadorInicial))
          if (destinoMejorRegate != null)
          {
            // Regatea;
            this.faseActual = FASE_REGATE;
double factorRemateJugador = sp.detalleMisJugadores()[indJugadorInicial].getRemate();
//          Posicion destino = new Posicion( posJugador.getX(), LIMITE_Y_JUGADOR_INICIAL);
//            Vector<ComandoInfo> comandosRegate = this.crearComandoRegate(sp, indJugadorInicial, this.destinoRegateJugadorInicial);
            Vector<ComandoInfo> comandosRegate = this.crearComandoRegate( indJugadorInicial, destinoMejorRegate, posJugador, factorRemateJugador);
            comandos.addAll(comandosRegate);
            
            // Actualizar nuevo destino del jugador
            this.lastDestinoRegate = destinoMejorRegate;
            
          }
          else
          {
            // Si hay rivales cerca no regatear, pasar al mejor jugador posicionado
            this.faseActual = FASE_PASE;
          }
}
else
{
  // Fase de regate, pero el jugador ya hab�a golpeado el bal�n
  if (this.lastDestinoRegate != null)
  {

//this.lastDistanciaBalonDestinoRegate = sp.balon().distancia(this.lastDestinoRegate);            
    
//    // Continuar con la direccion del regate.
//    comandos.add( new ComandoInfo(new ComandoIrA(indJugadorInicial, this.lastDestinoRegate), "Continuar el regate iniciado Jugada ensayada de "+this.indJugadorInicial + " hacia = " + this.lastDestinoRegate));
    // Continuar con la direccion del regate HACIA LA PELOTA.
    comandos.add( new ComandoInfo(new ComandoIrA(indJugadorInicial, sp.balon()), "Continuar el regate iniciado Jugada ensayada de "+this.indJugadorInicial + " hacia LA PELOTA , y no hacia el destino original = " + this.lastDestinoRegate));
  }
}
          // Desmarcar jugadores
          int indJugador;
          Posicion destino;
          for (int i = 0; i < this.indJugadoresDesmarque.length; i++)
          {
            indJugador = this.indJugadoresDesmarque[i];
            destino = this.destinoRegateJugadoresDesmarque[i];
            comandos.add( new ComandoInfo(new ComandoIrA(indJugador, destino), "Desmarcar jugadores Jugada ensayada "+(i+1)+" de "+this.indJugadoresDesmarque.length));
          }
        }
      }
      if (this.faseActual == FASE_PASE)
      {
//System.err.println("FASE_PASE");

        int indJugador = indJugadorInicial;
        if (Termineitor.puedeRematar(indJugador, sp, Termineitor.COMANDO_PASE))
        {
          // Calcular mejor pase 
          boolean permitirDesmarque = PASE_PERMITIR_DESMARQUE;
          DatosPaseJugador datosPase = calcularMejorPaseJugador(indJugador, sp, this.indJugadoresDesmarque, permitirDesmarque);
          comandos.add(new ComandoInfo( new ComandoGolpearBalon(indJugador, datosPase.getDestino(), datosPase.getFactorFuerza(), datosPase.getAnguloVertical()),
                                        "Pase Mejorado Jugada ensayada"));
          
this.faseActual = FASE_RECEPCION;
this.datosPase = datosPase;
          
        }
      }
      if (this.faseActual == FASE_RECEPCION)
      {
        // Situar al receptor del pase
        if (this.datosPase != null)
        {
          Posicion destino = datosPase.getDestino();
          int indJugadorDestinoDelPase = this.datosPase.getIndJugadorDestino();
          
          if (Termineitor.puedeRematar( indJugadorDestinoDelPase, sp, COMANDO_JUGADA))
          {
            // El receptor del pase puede controlar el bal�n
            this.faseActual = FASE_FINAL;
            this.datosPase = null;
          }
          else
          {
          
          
            if (ACHIQUE_ANTICIPADO_RIVALES)
            {
              // Metodo a usar SIEMPRE, no solo si la pelota va rapida
              destino = calcularAchiqueAnticipadoRival( indJugadorDestinoDelPase, destino,
                                                             sp, this.lastVelocidadBalon);
            }
          
            comandos.add( new ComandoInfo(new ComandoIrA(indJugadorDestinoDelPase, destino), "Recepcion del pase Jugada ensayada "));
          
            if (Termineitor.puedeRematar( indJugadorDestinoDelPase, sp, COMANDO_JUGADA))
            {
              // El receptor del pase puede controlar el bal�n
              this.faseActual = FASE_FINAL;
              this.datosPase = null;
            }
            else
            {
              double newDistanciaBalonPase = this.datosPase.getDestino().distancia(sp.balon());
              if (newDistanciaBalonPase >= this.lastDistanciaBalonPase)
                this.faseActual = FASE_NINGUNA;
              
              this.lastDistanciaBalonPase = newDistanciaBalonPase;
            }
          }
        }
        
      }
      if (this.faseActual == FASE_TIRO)
      {
        Posicion posJugador = sp.misJugadores()[indJugadorInicial];
Posicion destinoDisparo = Constantes.centroArcoSup;
        boolean tiroAlto = true;
        if (destinoDisparo.distancia(posJugador) < TIRO_ALTO_LIMITE)
          tiroAlto = false;
        
double fuerza = 1;
        if (Termineitor.puedeRematar(indJugadorInicial, sp, Termineitor.COMANDO_TIRO))
        {
          this.faseActual = FASE_TIRO;
          Vector<ComandoInfo> comandoTiroCercano; 
          comandoTiroCercano = crearComandoTiroCercano(indJugadorInicial, destinoDisparo, sp.balon(), sp.misJugadores(), sp.rivales(), sp.detalleMisJugadores()[indJugadorInicial].getRemate());
          comandos.addAll(comandoTiroCercano);
//          comandos.add(new ComandoInfo( new ComandoGolpearBalon(indJugadorInicial, destinoDisparo, fuerza, tiroAlto), "Tiro Jugada ensayada"));
        }
      }
      
    }
    
this.lastBalon = sp.balon();
    
    return comandos;
  }
  public String getNombreJugada( )
  {
    return this.nombreJugada;
  }
  
  public String getFaseJugada( )
  {
    String fase = "Fase Ninguna";
    if (this.faseActual == FASE_INICIAL)
      fase = "Fase Inicial";
    else if (this.faseActual == FASE_INICIAL)
      fase = "Fase Inicial";
    else if (this.faseActual == FASE_REGATE)
      fase = "Fase Regate";
    else if (this.faseActual == FASE_PASE)
      fase = "Fase Pase";
    else if (this.faseActual == FASE_RECEPCION)
      fase = "Fase Recepcion";
    else if (this.faseActual == FASE_TIRO)
      fase = "Fase Tiro";
    else if (this.faseActual == FASE_FINAL)
      fase = "Fase Final";
    
    return fase;
  }
  
  protected JugadaEnsayada[] crearJugadasEnsayadas( )
  {
    return new JugadaEnsayada[0];
  }
}
public interface JugadaEnsayada
{
  public String getNombreJugada( );
  public String getFaseJugada( );
  public boolean checkIniciarJugada( SituacionPartido sp);
  public boolean checkPararJugada( SituacionPartido sp);
  public Vector<ComandoInfo> getComandosJugada( SituacionPartido sp );
  
}  

public class DatosPaseJugador extends DatosPase
{
  private int indJugadorOrigen;
  private int indJugadorDestino;

  public DatosPaseJugador(int indJugadorOrigen, int indJugadorDestino, DatosPase datosPase)
  {
    this(indJugadorOrigen, indJugadorDestino, datosPase.getOrigen(), datosPase.getDestino(),
         datosPase.getFactorFuerza(), datosPase.getAnguloVertical(), datosPase.getEvaluacionPase());
  }
  
  public DatosPaseJugador(int indJugadorOrigen, int indJugadorDestino, Posicion origen, Posicion destino, 
                          double factorFuerza, double anguloVertical, int evaluacionPase)
  {
    super(origen, destino, factorFuerza, anguloVertical, evaluacionPase);
    this.indJugadorOrigen = indJugadorOrigen;
    this.indJugadorDestino = indJugadorDestino;
  }

  protected int getIndJugadorOrigen()
  {
    return indJugadorOrigen;
  }
    
  protected int getIndJugadorDestino()
  {
    return indJugadorDestino;
  }
    
  public String toString()
  {
    String str = "DatosPaseJugador = ";
    str = str + "indJugadorOrigen = "+indJugadorOrigen;
    str = str + "\t indJugadorDestino = "+indJugadorDestino;
    str = str + "\t : "+ super.toString();
    return str;
  }

}

}